题目描述:
给定一个非负整数数组和一个整数m,你需要将这个数组分成m个非空的连续子数组。设计一个算法使得这m个子数组各自和的最大值最小。
示例 1:
输入: nums = [7,2,5,10,8] m = 2 输出: 18 解释: 一共有四种方法将nums分割为2个子数组。 其中最好的方式是将其分为[7,2,5]和[10,8], 因为此时这两个子数组各自的和的最大值为18,是所有情况中最小的。
Java代码实现:
class Solution {
public int splitArray(int[] nums, int m) {
int left = 0;
int right = 0;
for (int i = 0; i < nums.length; i++) {
right += nums[i];
if (left < nums[i]) {
left = nums[i];
}
}
while (left < right) {
int mid = left + (right - left) / 2;
int sum = 0;
int count = 1;
for (int i = 0; i < nums.length; i++) {
if (sum + nums[i] > mid) {
count++;
sum = nums[i];
} else {
sum += nums[i];
}
}
if (count <= m) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
解题思路:
这道题需要将数组分成m个子数组,且各自和的最大值最小,因此可以使用二分查找来解决。
首先,通过遍历整个数组,可以得到数组的左右边界。左边界是数组中的最大值,右边界是数组所有元素的和。这里使用了一个贪心的策略,将数组分成一个元素一个元素的子数组时,可以得到最大的最小和值。
然后,在[left, right]区间进行二分查找,计算mid值,以mid值作为各子数组和的最大值,计算当前分割数组的子数组数量。如果子数组数量小于等于m,则说明当前mid值合法,将右边界移到mid值,否则说明mid值过小,需要将左边界移到mid+1。当左边界等于右边界时,退出循环,返回此时的左边界即可。
这里需要注意的是,二分查找的循环条件是left < right,而不是left <= right。这是因为当left=right时,说明此时的mid值已经被计算过了,如果再进行一次计算,会导致重复计算,因此需要将循环条件限制在left<right。