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


    本文涉及的基础知识点

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

    LeetCode 2025分割数组的最多方案数

    给你一个下标从 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 左边增加 k - nums[i]
    i>=p右边增加 k - nums[i]

    大致流程

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

    如果i在左边则初始mModifyLeftLeftSubRight[-iSub]就是方案数
    如果i在右边则初始mModifyRightLeftSubRight[iSub]就是方案数

    变量解释

    llTotanums的和
    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 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 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    }

    2023年4月旧代码

    class Solution {
    public:
    int waysToPartition(vector& nums, int k) {
    long long llSum = 0;
    std::unordered_map 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 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    };

    扩展阅读

    视频课程

    有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
    https://edu.csdn.net/course/detail/38771

    如何你想快

    速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
    https://edu.csdn.net/lecturer/6176

    相关下载

    想高屋建瓴的学习算法,请下载《闻缺陷则喜算法册》doc版
    https://download.csdn.net/download/he_zhidan/88348653

    鄙人想对大家说的话
    闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
    墨家名称的来源:有所得以墨记之。
    如果程序是一条龙,那算法就是他的是睛

    测试环境

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

    VS2022 C++17

  • 相关阅读:
    CSS 媒体查询 @media【详解】
    RTU远程终端控制系统S274
    Linux必备技能:掌握的必会命令
    whisper 安装
    laravel自定义日志保存文件加上日期
    安达发APS|APS智能排程软件的核心优势
    Ubuntu server 24 (Linux) 安装部署smartdns 搭建智能DNS服务器
    【Git】git的安装与使用教程
    ffmpeg分离左右声道到多音轨
    ArcGIS API for JavaScript 4.x 实现动态脉冲效果
  • 原文地址:https://blog.csdn.net/he_zhidan/article/details/134031665