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.
Approach #1: Plan Recursion (Up to Bottom)
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
const int n = nums.size();
sums_ = vector<int>(n);
mem_ = vector<vector<int>>(n, vector<int>(m + 1, INT_MAX));
sums_[0] = nums[0];
for (int i = 1; i < n; ++i) {
sums_[i] = nums[i] + sums_[i-1]; // prefix sum
}
return helper(nums, n-1, m);
}
private:
int helper(const vector<int>& nums, int k, int m) {
if (m == 1) return sums_[k]; // split one group.
if (m > k + 1) return INT_MAX; // split groups more than the elements.
if (mem_[k][m] != INT_MAX) return mem_[k][m]; // return the have memeried status
int ans = INT_MAX;
for (int i = 0; i < k; ++i)
ans = min(ans, max(helper(nums, i, m-1), sums_[k] - sums_[i])); // calculation minimize largest sum.
return mem_[k][m] = ans;
}
vector<vector<int>> mem_;
vector<int> sums_;
};
Runtime: 116 ms, faster than 2.38% of C++ online submissions for Split Array Largest Sum.
Approach #2: DP (Bottom to Up)
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
const int n = nums.size(); // if I don't use const it will not compile successfully.
vector<int> sum(n+1);
vector<vector<int>> dp(m+1, vector<int>(n, INT_MAX));
sum[0] = nums[0];
for (int i = 1; i < n; ++i)
sum[i] = sum[i-1] + nums[i];
for (int i = 0; i < n; ++i)
dp[1][i] = sum[i]; // the status with only one group;
for (int i = 2; i <= m; ++i) {
for (int j = i-1; j < n; ++j) {
for (int k = 0; k < j; ++k) {
dp[i][j] = min(dp[i][j], max(dp[i-1][k], sum[j] - sum[k]));
}
}
}
return dp[m][n-1];
}
};
Runtime: 116 ms, faster than 2.38% of C++ online submissions for Split Array Largest Sum.
Approach #3: Binary Search:
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
int l = 0, r = 0;
for (auto& num : nums) { // if don't use auto& it won't compile successfully.
l = max(l, num);
r += num; // the answer must exsit bewteen in the l and r.
}
while (l <= r) {
int mid = l + (r - l) / 2;
if (canSplit(nums, m, mid))
r = mid - 1;
else
l = mid + 1;
}
return l;
}
private:
bool canSplit(vector<int>& nums, int m, int mid) {
int c = 1;
int sum = 0;
for (auto& num : nums) {
sum += num;
if (sum > mid) {
c++;
sum = num;
}
}
return c <= m;
}
};
Runtime: 4 ms, faster than 49.91% of C++ online submissions for Split Array Largest Sum.