410. Split Array Largest Sum
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.
方法1: dynamic programming
思路:
首先这种寻找最佳切法的题目,一般都可以一步一步缩小成子问题,当找到子问题的最优后,再思考如何再size增加时找到下一步最优解。在这道题里,有两种缩小问题的方向,第一是vector长度可能缩小,第二是m可以缩小。先看第一种,对与[0, j]范围内的vector,m的最优解如何得到?我们只需要考虑如何切最后一段,遍历所有可能切的位置,每一次算出最后一段的sum,和之前一段切m - 1段的最优解进行max比较,就是这种切法不得不承受的max。而遍历所有切的位置取的min,就是[0, j]内切m段能获得的min max sum。
这里令人头秃的三重循环各自含义如下:
i : 需要切的段数
j : 整体字符串长度
k:最后一切的前一个位置,也就是切成[0,…,k], [k + 1, j]的样子
vector<vector<\int>> dp(m, vector<\int>(n + 1, INT_MAX)): 这里按理说第一行存成presum就可以,也就是m = 1时的dp解。但是由于要取最小值,初始化成INT_MAX就不能用来累计presum了,会overflow,借助一个单独的presum来完成。
initialiaztion:INT_MAX, 第一行为presum,第一列是0。
transfer:尝试每一个最后一切的位置k,dp[i][j] = min(dp[i][j], max(dp[i - 1][k], dp[0][j] - dp[0][k]);
return: dp.back().back()
Complexity
Time complexity: O(m * n ^ 2)
Space complexity: O(m * n)
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
int n = nums.size();
if (n == m) return *max_element(begin(nums), end(nums));
vector<vector<int>> dp(m, vector<int>(n + 1, INT_MAX));
vector<int> presum(n + 1, 0);
for (int j = 1; j < n + 1; j++) {
presum[j] = presum[j - 1] + nums[j - 1];
dp[0][j] = presum[j];
}
for (int i = 0; i < m; i++) dp[i][0] = 0;
for (int i = 1; i < m; i++) {
for (int j = 1; j < n + 1; j++) {
for (int k = i - 1; k < j; k++) {
dp[i][j] = min(dp[i][j], max(dp[i - 1][k], dp[0][j] - dp[0][k]));
}
}
}
return dp.back().back();
}
};