0
点赞
收藏
分享

微信扫一扫

C++前缀和算法的应用:分割数组的最多方案数 原理源码测试用例


本文涉及的基础知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

题目

给你一个下标从 0 开始且长度为 n 的整数数组 nums 。分割 数组 nums 的方案数定义为符合以下两个条件的 pivot 数目:
1 <= pivot < n
nums[0] + nums[1] + … + nums[pivot - 1] == nums[pivot] + nums[pivot + 1] + … + nums[n - 1]
同时给你一个整数 k 。你可以将 nums 中 一个 元素变为 k 或 不改变 数组。
请你返回在 至多 改变一个元素的前提下,最多 有多少种方法 分割 nums 使得上述两个条件都满足。
示例 1:
输入:nums = [2,-1,2], k = 3
输出:1
解释:一个最优的方案是将 nums[0] 改为 k 。数组变为 [3,-1,2] 。
有一种方法分割数组:

  • pivot = 2 ,我们有分割 [3,-1 | 2]:3 + -1 == 2 。
    示例 2:
    输入:nums = [0,0,0], k = 1
    输出:2
    解释:一个最优的方案是不改动数组。
    有两种方法分割数组:
  • pivot = 1 ,我们有分割 [0 | 0,0]:0 == 0 + 0 。
  • pivot = 2 ,我们有分割 [0,0 | 0]: 0 + 0 == 0 。
    示例 3:
    输入:nums = [22,4,-25,-20,-15,15,-16,7,19,-10,0,-13,-14], k = -33
    输出:4
    解释:一个最优的方案是将 nums[2] 改为 k 。数组变为 [22,4,-33,-20,-15,15,-16,7,19,-10,0,-13,-14] 。
    有四种方法分割数组。
    参数范围
    n == nums.length
    2 <= n <= 105
    -105 <= k, nums[i] <= 105

分析

原理

nums[0,p)-nums[p,m_c) 如果等于0,则符合条件。修改nums[i]

i <p

左边增加 k - nums[i]

i>=p

右边增加 k - nums[i]

大致流程

先获取初始状态(未修改)所有数的和。
枚举p,左边减右边的放到mModifyRightLeftSubRight中。
初始状态下,mModifyRightLeftSubRight[0]就是分割方案数。
枚举修改nums[i]。

如果i在左边

则初始mModifyLeftLeftSubRight[-iSub]就是方案数

如果i在右边

则初始mModifyRightLeftSubRight[iSub]就是方案数

变量解释

llTota

nums的和

mModifyLeftLeftSubRight;

符合i < p,各方案nums[0,p)-nums[p,m_c)

mModifyRightLeftSubRight

符合i>=p,各方案nums[0,p)-nums[p,m_c)

时间复杂度

两次循环,时间复杂度都是O(n),故总时间复杂度是O(n)。

代码

核心代码

class Solution {
 public:
 int waysToPartition(vector& nums, int k) {
 m_c = nums.size();
 long long llTotal = std::accumulate(nums.begin(), nums.end(), 0LL);
 std::unordered_map<long, int> mModifyRightLeftSubRight;
 //[0,p)左半部分,[p,m_c)右半部分
 long long llR = 0;//llR是nums[p,m_c)的和
 for (int p = m_c - 1; p > 0; p–)
 {
 llR += nums[p];
 const long long llLeft = llTotal - llR;
 mModifyRightLeftSubRight[llLeft - llR]++;
 }
 int iRet = mModifyRightLeftSubRight[0];//不改变任何数,能拆分的数量
 std::unordered_map<long, int> mModifyLeftLeftSubRight;
 //修改nums[p]
 llR = 0;
 for (int i = m_c - 1; i >= 0; i–)
 {
 int iSub = (k - nums[i]);
 const int cur = mModifyRightLeftSubRight[iSub]+ mModifyLeftLeftSubRight[-iSub];
 iRet = max(iRet, cur);
 llR += nums[i];
 const long long llLeft = llTotal - llR;
 mModifyRightLeftSubRight[llLeft - llR]–;
 mModifyLeftLeftSubRight[llLeft - llR]++;
 }
 return iRet;
 }
 int m_c;
 };

测试用例

template
 void Assert(const vector& v1, const vector& v2)
 {
 if (v1.size() != v2.size())
 {
 assert(false);
 return;
 }
 for (int i = 0; i < v1.size(); i++)
 {
 assert(v1[i] == v2[i]);
 }
 }template
 void Assert(const T& t1, const T& t2)
 {
 assert(t1 == t2);
 }int main()
 {
 Solution slu;
 vector nums;
 int k;
 int res;
 nums = { 0,0,0 };
 k = 1;
 res = slu.waysToPartition(nums, k);
 Assert(2, res);
 nums = { 0,0,0,0 };
 k =3;
 res = slu.waysToPartition(nums, k);
 Assert(3, res);
 nums = { -2, -1, 3, 4, 5 };
 k = 3;
 res = slu.waysToPartition(nums, k);
 Assert(0, res);
 nums = { -2, -1, 3, 4, 5 };
 k = 2;
 res = slu.waysToPartition(nums, k);
 Assert(0, res);
 nums = { -2, -1, 3, 4, 5 };
 k = -3;
 res = slu.waysToPartition(nums, k);
 Assert(0, res);
 nums = { -1, -1, 3, 4, 5 };
 k = -3;
 res = slu.waysToPartition(nums, k);
 Assert(1, res);
 nums = { -1,3 };
 k = 3;
 res = slu.waysToPartition(nums, k);
 Assert(1, res);
 nums = { 5,4,3 };
 k = 8;
 res = slu.waysToPartition(nums, k);
 Assert(0, res);
 nums = { 5,4,3 };
 k = 1;
 res = slu.waysToPartition(nums, k);
 Assert(1, res);nums = { 5,4,3 };
k = 7;
res = slu.waysToPartition(nums, k);
Assert(1, res);
nums = { 3,5,4 };
k = 1;
res = slu.waysToPartition(nums, k);
Assert(1, res);
nums = { 3,5,4 };
k = 1;
res = slu.waysToPartition(nums, k);
Assert(1, res);
nums = { 3,5,4 };
k = 0;
res = slu.waysToPartition(nums, k);
Assert(0, res);

//CConsole::Out(res);}

2023年4月旧代码

class Solution {
 public:
 int waysToPartition(vector& nums, int k) {
 long long llSum = 0;
 std::unordered_map<long long, int> mSumNum;//各前缀和数量
 vector vSum;
 for (int i = 0; i < nums.size(); i++)
 {
 llSum += nums[i];
 vSum.emplace_back(llSum);
 if (i + 1 < nums.size())
 {
 mSumNum[llSum]++;
 }
 }
 int iRet = 0;
 if (0 == llSum % 2)
 {
 iRet += mSumNum[llSum / 2];
 }
 const long long llNewSum = llSum + k - nums.back();
 if (0 == llNewSum % 2)
 {
 iRet =max(iRet, mSumNum[llNewSum / 2]);
 }
 std::unordered_map<long long, int> mRightSumNum;
 for (int i = nums.size() - 2; i >= 0; i–)
 {
 const long long& llCurSum = vSum[i];
 mSumNum[llCurSum]–;
 mRightSumNum[llCurSum]++;
 const long long llNewSum = llSum + k - nums[i];
 if (llNewSum & 1)
 {//改变后是奇数
 continue;
 }int iCurRet = mSumNum[llNewSum / 2];
		iCurRet += mRightSumNum[llNewSum / 2 - (k - nums[i])];
		iRet = max(iRet, iCurRet);
	}
	return iRet;
}};


相关下载

想高屋建瓴的学习算法,请下载《闻缺陷则喜算法册》doc版

鄙人想对大家说的话

闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。

墨家名称的来源:有所得以墨记之。

如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境:

VS2022 C++17

C++前缀和算法的应用:分割数组的最多方案数 原理源码测试用例_开发语言


举报

相关推荐

0 条评论