关键词:二分法
问题描述:
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.
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.
即将一个数组分为m个连续的子序列,使m个子序列各自的和的最大值最小化。
解题思路:
记数组nums的和为su
在[0, su + 1)区间中进行二分,每次二分比较大小的函数为:判断中值是否能作为m份子序列的最大值。
首先回忆一下数组的二分法:
int lower_bound(vector<int> &a, int x){
int lo = 0, hi = a.size();
while(lo < hi){
int mi = (lo + hi) >> 1;
int v = a[mi];
x <= v ? hi = mi : lo = mi + 1;
}
return hi;
}
int upper_bound(vector<int> &a, int x){
int lo = 0, hi = a.size();
while(lo < hi){
int mi = (lo + hi) >> 1;
int v = a[mi];
x < v ? hi = mi : lo = mi + 1;
}
return --hi;
}
以lower_bound为例,[0, lo)为小于x的所有元素,[hi, n)为大于或等于x的所有元素。
在二分的过程中,这个约束没有改变。
二分法结束时,lo = hi。
要求得第一个大于或等于x的元素,根据约束可知,所求元素的位置为hi(lo).
以upper_bound为例,[0, lo)为小于或等于x的所有元素,[hi, n)为大于x的所有元素。
在二分的过程中,这个约束没有改变。
二分法结束时,lo = hi。
要求得最后一个小于或等于x的元素,根据约束可知,所求元素的位置为hi-1(lo-1).
在本问题中,使用的二分法与lower_bound相近。
参考代码:
class Solution {
public:
bool check(vector<int> &nums, int m, long long q){
long long s = 0;
int use = 0;
for (int i = 0; i < nums.size(); ++i){
int h = nums[i];
if (h > q)return false;
if (s + h > q){
++use;
if (use >= m)return false;
s = 0;
}
s += h;
}
return true;
}
int splitArray(vector<int>& nums, int m) {
long long su = 0;
for (int h : nums)su += h;
long long lo = 0;
long long hi = su + 1;
// [lo, hi)
while (lo < hi){
long long mi = (lo + hi) >> 1;
if (check(nums, m, mi)){ // if (a[mi] <= x)
// 可以更小
hi = mi;
}else{
// 可以更大
lo = mi + 1;
}
}
return hi;
}
};