抽象出 $x、f(x)、target$ 框架

此题思路同 1011. 在 D 天内送达包裹的能力 - 力扣(LeetCode) 以及 875. 爱吃香蕉的珂珂 - 力扣(LeetCode),同样根据 laluladong的总结 写出来的。一开始还给它排了序,提交时有些例子不过,才知道要求连续子数组,删除排序操作就可以了。

什么是自变量 $x$,对应函数 $f(x)$ 以及 $target$

题目要求m个子数组的各自和的最大值最小,其实也就说在满足分组数目的条件下,尽可能的让分组的最大和最小;所以完全可以把分组的最大和设置为变量x,所以在计算 $f(x)$ 时,将 $x$ 理解为每个分组和的限制条件即可。

对应函数 $f(x)$ 是什么

  • $f(x)$ 就对应的是分组和限制条件 $x$ 下的分组数目,很显然,$x$ 越大分组数目越少,$x$ 越小,分组数目越多(注意:不同的 $x$ 可能对应相同的分组数

二分怎么找?

对于返回值判断的问题取决于二分查找区间的设计,我喜欢左右都闭的情况,以下只针对该情况分析。

  • target在哪:这道题,一定存在一个 $x$ 使得,$f(x)==target$,而且可能存在多个 $x$ 满足,所以要找的是左边界。
  • 这一点与 1011. 在 D 天内送达包裹的能力 - 力扣(LeetCode) 以及 875. 爱吃香蕉的珂珂 - 力扣(LeetCode) 这两道题是不同的,吃香蕉和运货物中不一定存在 $x$ 满足 $f(x)==target$,因此对于返回条件 $left==right$,需要根据 $left$ 对应的函数值来做出判断。
  • 虽然,该题与运送货物极为相似,但还是略有不同;该题一定可以将nums分成 $K$ 组,只不过不同的分法对应不同的限制条件,但是运送货物不一定能够保证在 $days$ 天完成,可能就是在 $days+1$ 或者 $days-1$ 天完成,所以为了满足条件,只能取 $days-1$ 对应的运输能力。
  • 自变量的左右边界:最少分成1组,对应的是数组和;最多分成nums.size()组,对应的是其中的最大值。

具体实现

class Solution {
public:
    int splitArray(vector<int>& nums, int k) {
        // 先排序
        // sort(nums.begin(),nums.end());
        vector<int> ans=minMax(nums);
        int left=ans[0],right=ans[1];
        while(left<right)
        {
            // f(x)递减,因为要求在特定的组数目,groupSum最小,找左边界,而且一定存在left或者mid==groupNum
            int mid=left+(right-left)/2;
            if(f(nums,mid)==k)
            {
                right=mid;
            }
            else if(f(nums,mid)>k)
                left=mid+1;
            else if(f(nums,mid)<k)
                right=mid-1;
        }
        return left;
    }
    // 构建f(x) 函数,自变量设置每一组的和,返回值是组数目
    int f(vector<int>&nums,int groupSum)
    {
        // 返回值分组数组
        int temp=0,group=0;
        for(int i=0;i<nums.size();i++)
        {
            temp+=nums[i];
            if(temp==groupSum)
            {
                temp=0;
                group++;
            }
            else if(temp>groupSum)
            {
                temp=0;
                group++;
                i--;
            }
        }
        // 如果索引完,结果temp还有值,则还需要一组
        if(temp>0)   //  if(temp<groupSum)
            group++;
        return group;
    }
    // 求左右边界,理论最小边界就是最大的元素值,这样分组数目最多
    // 理论最大边界就是所有元素之和,这样就只需要一组即可
    vector<int> minMax(vector<int>&nums)
    {
        int sum=0,maxNum=0;
        for(int i=0;i<nums.size();i++)
        {
            sum+=nums[i];
            maxNum=maxNum>nums[i] ? maxNum : nums[i];
        }
        return {maxNum,sum};
    }
};

$\cdots$ end $\cdots$