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.
// Binary Search + Greedy
bool canSplit(int target, vector<int>& nums, int m) {
int cnt = 1;
long long curSum = 0;
for (int i = 0; i < nums.size(); ++i) {
curSum += nums[i];
if (curSum > target) {
curSum = nums[i];
++cnt;
if (cnt > m) return false;
}
}
return true;
}
int splitArray(vector<int>& nums, int m) { // time: O(nlogn); space: O(1)
long long left = 0, right = 0;
for (int num : nums) {
left = max(left, (long long)num);
right += num;
}
if (m == 1) return (int)right;
while (left < right) {
long long mid = left + (right - left) / 2;
if (canSplit(mid, nums, m)) right = mid;
else left = mid + 1;
}
return (int)left;
}
// Dynamic programming
int splitArray(vector<int>& nums, int m) { // time: O(m * n^2); space: O(m * n)
int n = nums.size();
vector<long> sums(n + 1, 0);
// dp[i][j] means the smallest max value in i subarrays split by the first j numbers in nums array
vector<vector<int> > dp(m + 1, vector<int>(n + 1, INT_MAX));
dp[0][0] = 0; // Initiailization
// build sums array
for (int i = 1; i <= n; ++i) {
sums[i] = sums[i - 1] + nums[i - 1];
}
for (int i = 1; i <= m; ++i) { // i subarrays
for (int j = 1; j <= n; ++j) { // first j numbers in nums array
for (int k = i - 1; k < j; ++k) { // the kth number in nums array
// sums[j] - sums[k] calculates the last subarray in current consideration
int val = max(dp[i - 1][k], (int)(sums[j] - sums[k])); // minmax
dp[i][j] = min(dp[i][j], val); // max
}
}
}
return dp.back().back();
}