LeetCode 410-Split Array Largest Sum
题干:
给定一个数组 n u m s nums nums和一个数 k k k,要求把 n u m s nums nums分割成 k k k个连续非空子数组。问最大子数组和能达到的最小值为多少(即,找到一个分割方法,使得用该方法分割出的最大子数组和是所有方法中最小的,输出这个最小值)。
Input: nums = [7,2,5,10,8], k = 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.
解:
同LeetCode1011货物装载,即:有一艘货船和若干货物,每个货物的重量是 n u m s [ i ] nums[i] nums[i],需要 k k k天将这些货物全部运走,问货船的最小载重量是多少。
设最大子数组和为 x x x。可以得到,最大子数组和越大,分割所需要的组数越小,最大子数组和越小,分割所需要的组数越多。所以最大子数组和和分割数组所需要的组数是一个单调不递增关系。
所以,可以定义 f ( x ) f(x) f(x)为:以最大子数组和为 x x x来分割数组,所需要的组数。
又由题,需要限制 f ( x ) = = m f(x)==m f(x)==m。
所以该题所求的是:满足 f ( x ) = = m f(x)==m f(x)==m的条件下的最小 x x x。画图可得求的是左边界。
x x x的取值范围: l e f t = m a x ( n u m s [ i ] ) , r i g h t = ∑ n u m s [ i ] left=max(nums[i]), right=\sum nums[i] left=max(nums[i]),right=∑nums[i]
int f(vector<int>& nums,int x){
int cnt = 1, res = x;
for(int i: nums){
if(res < i){
++cnt;
res = x;
}
res -= i;
}
return cnt;
}
int splitArray(vector<int>& nums, int k) {
int left = 0, right = 0;
for(int i: nums){
left = max(left, i);
right += i;
}
right += 1;
int ans = left;
while(left < right){
int mid = left + ((right - left) >> 1);
int cnt = f(nums, mid);
if(cnt == k){
ans = mid;
right = mid;
}
else if(cnt < k) right = mid;
else if(cnt > k) left = mid + 1;
}
return ans;
}