Description:
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)
Example:
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.
Link:
https://leetcode.com/problems/split-array-largest-sum/
解题方法:
二分法:
假设给定一个值n
, 来对整个数组进行划分,要求每个子数组的和都不超过n
,那么能划分出多少个子数组
肯定存在一个下限m_min
(即最少能划分出几个子数组),而这道题其实就是求当给定一个m的时候,n最小是多少。
我们可以知道,当n越大,m_min
就越小,当n
等于整个数组的和的时候,m_min == 1
。所以我们知道n
的最大值为整个数组之和
。
当n越小,m_min
就越大,因为每个子数组能包含的数就越少,就要做更多的划分。当n < 数组最大的数
的时候,我们甚至都不能划分,所以综上所述,数组最大的数<=n<=数组之和
。
那么当给定一个n的时候,我们可以求出其能做出的最小划分m_min
:用贪心就可以求出,时间复杂度为nums.size()
。
所以这道题就变成了,在一个递增区间查找n
, 使得m_min <= m
, 所以,我们可以用二分查找来解决这道题。
Time Complexity:
NlogN
完整代码:
int splitArray(vector<int>& nums, int m) {
long start = 0, end = 0;
for(auto num: nums) {
start = start > num ? start : num;
end += num;
}
while(start + 1 < end) {
long mid = start + (end - start) / 2;
long checkR = check(nums, mid);
if(checkR <= m)
end = mid;
else
start = mid;
}
if(check(nums, start) <= m)
return start;
else
return end;
}
int check(vector<int>& nums, long m) {
long sum = 0;
int cnt = 1;
for(int i = 0; i < nums.size(); i++) {
sum += nums[i];
if(sum > m) {
sum = nums[i];
cnt++;
}
}
return cnt;
}
网友评论