410. Split Array Largest Sum
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:
Given m satisfies the following constraint: 1 ≤ m ≤ length(nums) ≤ 14,000.
题解
本题是说给定一个非负整数数组A和一个正整数m,将A分为m个子区间,显然有很多种分法,每一种划分,都可以找到一个子区间和的最大值sum,现在要求的是在各种划分中,sum的最小值。
本题可以使用二分答案的方法来做。要求这个最小值是比较难的,但是给定一个值x,判断是否存在一种划分使得每个子区间的和都不超过x,这是容易的。我们可以贪心地遍历一遍数组,不断求和,直到和超过了x值,再新分出一个子区间,最后检查划分出的子区间数是否超过了m。这个检查的时间复杂度为O(n).
然后就可以不断的询问x是否满足上述条件,如果满足说明我们要求的解不超过x,否则说明要求的解大于x,这就构成了一个二分的条件。我们先猜测x属于一个足够大的范围,然后检查中间值是否满足条件,不管结果如何,我们都可以将猜测区间减半。如此不断的缩减区间,就得到了最后的解。因为int的值不超过2^31,所以需要O(log(2^31))=O(1)次检测,因此算法复杂度是O(n)的。
代码:
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
unsigned lo = 1, hi = 0x7fffffff;
while (lo < hi) {
unsigned mi = (lo+hi)/2;
if (check(nums, m, mi)) hi = mi;
else lo = mi+1;
}
return lo;
}
bool check(vector<int>& nums, int m, unsigned x) {
unsigned sum = 0;
int cnt = 1;
for (int i = 0; i < nums.size(); i++) {
if (sum + nums[i] <= x) {
sum += nums[i];
}
else {
cnt += 1;
if (nums[i] > x) return false;
sum = nums[i];
if (cnt > m) return false;
}
}
return cnt <= m;
}
};