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)
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部分,假设有n种划分,假设max[i]为第i中情况能取到的最大值,求Math.min(max[0], max[1], ... max[i], ... , max[n-1])。
思路一:暴力求解,将取得各种划分情况,依次求解,但会TLE,程序如下所示:
class Solution {
private int max = Integer.MAX_VALUE;
public int splitArray(int[] nums, int m) {
int[] parts = new int[m+1];
backTracing(nums, 0, m, parts);
return max;
}
public void backTracing(int[] nums, int begin, int m, int[] parts){
if (begin > nums.length || m < 0){
return;
}
if (m == 0){
if (begin < nums.length){
return;
}
int cur = 0;
for (int val : parts){
cur = Math.max(cur, val);
}
max = Math.min(max, cur);
return;
}
for (int i = begin; i < nums.length; ++ i){
parts[m] += nums[i];
if (parts[m] > max){
break;
}
backTracing(nums, i + 1, m - 1, parts);
}
parts[m] = 0;
}
}
思路二:二分查找,通过二分查找算法直接查找目标值。
假设max为nums数组的最大值,sum为nums数组所有元素之和,可知,所求的各个情况最大值中的最小值(有点绕?但确实是这样)介于max和sum之间(含边界)。
class Solution {
public int splitArray(int[] nums, int m) {
int left = 0, right = 0;
for (int val : nums){
left = Math.max(left, val);
right += val;
}
int mid = 0;
while (left <= right){
mid = left + ((right - left) >> 1);
if (contains(nums, mid, m)){
right = mid - 1;
}
else {
left = mid + 1;
}
}
return left;
}
public boolean contains(int[] nums, int target, int m){
int total = 0, cnt = 0;
for (int val : nums){
total += val;
if (total > target){
total = val;
cnt ++;
if (cnt > m - 1){
return false;
}
}
}
return true;
}
}
本题已经说明数组元素为非负数,因此思路二可行;如果含负数,思路二不行,思路一仍然可行,因此思路一更具有通用性。