题目:
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.
题目给出一个数组nums和分割数m,将nums分割成m份,可以用不同的分割方法,要求找出m份数组中最大和的最小值result。
参考了网上二分法的算法:假设数组nums的长度为n,整个数组最多分成n份,此时这n份数组中的最大和即为nums数组中最大的元素,且是最小的最大和(因为只有一种分法),result=max(nums[i]);整个数组最少只分成一份,此时最小的最大和为所有元素之和(只有一种分法),result=sum(nums[i])。所以我们要求的result值应该是介于max(nums[i])和sum(nums[i])之间的,使用二分法来求result:求出当前范围内的中间元素mid,判断result值是否可能等于mid,如果可能,缩小范围,high=mid,求出更小的可行解;如果不可能,说明result是一个更大的数,low=mid+1。直到low=high。
判断result值是否可能等于mid:从前往后遍历数组,将前i个元素相加得到sum,如果sum+nums[i+1]不大于mid,将nums[i+1]加到sum中。否则,说明前i个元素必须被分成一份才能使result值不超过mid,份数加一,从i+1个元素开始继续遍历,判断遍历完后的份数是否超过m,如果超过,则说明result值不能等于mid,否则说明result值可能等于mid。
判断result值是否可能等于mid的时间复杂度为o(n),二分法的时间复杂度为o(logn),所以算法的时间复杂度为o(nlogn)。要注意Note中的提示,low,high,mid,result的值可能非常大,为了表示它们,要使用长整型变量。
Accepted的代码:
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
//得到最大值和最小值
long int low=nums[0],high=0;
for(int i=0;i<nums.size();i++)
{
if(nums[i]>low) low=nums[i];
high+=nums[i];
}
//二分搜索
while(low<high)
{
long int mid=(low+high)/2;
if(isTrue(nums,mid,m)) high=mid;
else low=mid+1;
}
return low;
}
//判断结果是否可能为mid
bool isTrue(vector<int>& nums,int mid,int m)
{
int count=1;//数组的分段数
int sum=0;//每小段的和
for(int i=0;i<nums.size();i++)
{
if(sum+nums[i]<=mid)
{
sum+=nums[i];
}
else
{
count++;
sum=nums[i];
if(count>m) return false;
}
}
return true;
}
};