力扣解题思路:410. 分割数组的最大值

410. 分割数组的最大值

思路:在这里插入图片描述
虽然第一反应是用动态规划,可是想半天还是想不出动态方程,所以放弃了。。。

但是点开标签发现可以采用二分法来解题,但是我们的数组是无序的,所以如何确定二分法的思路呢?

我们可以首先采用贪心策略,先指定一个中间数mid,我们将数组不断划分成小于这个中间数的多个数组,划分方式使用贪心策略,也就是小于该数时不断往里放,直到满了就开辟另一个子数组再往里放。

由于此时我们并没有指定划分多少个,所以按照这种贪心策略可能划分的数组个数不等于题目要求的个数,如果我们划分的个数过大,我们就可以提高mid(增大子数组容量),相反如果我们划分的个数过小,我们就可以减小mid。

那么问题来了,如果我们划分的个数刚好等于题目要求个数,这时是该直接返回答案吗?当然不是!因为我们要求子数组和最小,所以我们还需要再缩小mid !!!

完整代码如下,细节见注释:

public int splitArray(int[] nums, int m) {
    /*
   二分法  //这部分参考自:https://leetcode-cn.com/problems/split-array-largest-sum/comments/

   nums = [7,2,5,10,8]
   m = 1,那么整个数组作为一部分,最小的最大值为 32
   m = n,那么每个元素作为一个子数组,从所有元素选取最大值,最小的最大值小为 10

   m 的取值范围为 1 <= m <= n,因此,最大值的最小值的范围为 [10, 32]

   我们利用二分法查找,找出符合 m 的最大值的最小的结果
   二分过程:
   left = 10;
   right = 32
   mid = (left + right) >>> 1 = 21(这个 21 就是一个子数组的最大容量)
   我们假设刚开辟的用来存储的子数组个数 cnt = 1
   那么根据贪心思想,我们将数组元素按顺序逐个往里放
   因此就有如下过程:
   7 < 21
   7 + 2 < 21
   7 + 2 + 5 < 21
   7 + 2 + 5 + 10 > 21
    至此,我们可以看出一个 21 容量的子数组是无法容纳整个数组元素的,因此我们需要开辟第二个子数组来存储剩下的数组元素
   cnt = cnt + 1 = 2
   10 < 21
   10 + 8 < 21
   我们发现,两个子数组可以将整个数组元素放入,而 cnt 刚好等于 m,因此 [7,2,5] 和 [10,8] 就是分割出来的两个子数组,最小的最大值为 18

   为什么是放入元素直到放不下为止?因为要求的是连续子数组,我们需要保证每个连续的子数组的元素和都尽可能的接近 21

   如果我们最终得到的 cnt > m,那么表示我们划分出太多的子数组,也就是意味着一个子数组的容量太少,我们需要再扩大容量,即 left = mid + 1,然后继续进行二分
   如果我们最终得到的 cnt < m,那么表示我们划分出太少的子数组,也就是意味着一个子数组的容量太大,需要减少容量,即 right = mid - 1
   */
   long left = 0;//采用long是因为测试用例中出现了个别比较大的数
   long right = 0;
   for(int num : nums){
       left = Math.max(left, num);
       right += num;
   }
   //时间复杂度 O(nlogn)
   while(left < right){
       long mid = (left + right) >>> 1;
       int c = 0;
       long temp = 0;
       for(int num : nums){
           if(temp + num > mid){
               c++;
               temp = 0;
           }
           temp += num;
       }
       //最终需要再 +1 ,因为最后的一个数组在上面的循环中不会被添加
       c++;
       //分割的子数组个数比目标个数大,那么意味着容量小的,需要调大容量
       if(c > m){
           left = mid + 1;
       }else{
           //容量过大或者容量刚好,则缩小最大和
           right = mid;
       }
   }
   return (int)left;//right
}

之前我不理解的点在于为什么要返回left,因为我不确定left是否最终会收缩到最小和,后来我发现如果我们划分的个数刚好等于题目要求个数,mid会左移直到刚刚好能等于某个子数组和,所以最终left会收缩到和right一致,也就是会收缩到最终答案。

还有动态规划法,之后再更新。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值