问题描述:
Given an array which consists of non-negative integers and an integer m, you can split the array into m non-empty continuous subarrays. Write an algorithm to minimize the largest sum among these m subarrays.
Note:
If n is the length of array, assume the following constraints are satisfied:
- 1 ≤ n ≤ 1000
- 1 ≤ m ≤ min(50, n)
Examples:
Input: nums = [7,2,5,10,8] m = 2 Output: 18 Explanation: There are four ways to split nums into two subarrays. The best way is to split it into [7,2,5] and [10,8], where the largest sum among the two subarrays is only 18.
解题思路:
刚看到这道题没有一点思路,于是参考了HaoxiangXu的解法
它的解法讲得非常清晰透彻了。
这里用到了二分法和贪婪算法来一起做。
首先将问题分成了两个子问题:
1. 给出一个数组arr,给定分割数m以及每个组的最大和MAX,能否用最多m个分割将数组分成子数组和不超过MAX的子数组?
2. 给出一个下界left,和一个上界right,一个未知的bool数组(B)以及一个使用i作为输入并且告诉你B[i]是否为true的API。若我们知道,一定存在一个下标k,使得i<k 时,B[i] 为false, i >= k时, B[i]为true。 找到k的最快的方式是什么?
对于第一个问题, 我们可以使用贪婪算法来解答。在组中加入当前数字n,判断当前的组合是否超出MAX,若超出,则将当前数字作为新的组的起点,更新新的组和为当前数字n,并且分割数减一(因为开始了新的组,我们需要用一个分割来分割两个组)。
注意此时要判断分割数是否小于0,若分割数小于0,则代表当前分割数无法分割出组和不超过MAX的子数组们。
对于第二个问题,我们很容易想到二分搜索。
回归到当前问题,我们需要寻找上界(right)和下界(left)。
最小的分割为只有一个元素,最大的分割为原数组,所以left = max(arr), right = sum(arr);
给大神跪了ORZ
代码:
class Solution { public: int splitArray(vector<int>& nums, int m) { long long left = 0, right = 0; for(int n : nums){ left = max(left, (long long)n); right += (long long)n; } while(left < right){ long long mid = left + (right - left)/2; if(isWork(nums, m-1, mid)) right = mid; else left = mid+1; } return left; } private: bool isWork(vector<int>& nums, int cuts, long long max){ int acc = 0; for(int n : nums){ if(n > max) return false; if(acc + n <= max) acc += n; else{ acc = n; cuts--; if(cuts < 0) return false; } } return true; } };