【LeetCode-410】分割数组的最大值
问题
给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。
设计一个算法使得这 m 个子数组各自和的最大值最小。
示例
输入:nums = [7,2,5,10,8], m = 2
输出:18
解释:
一共有四种方法将 nums 分割为 2 个子数组。 其中最好的方式是将其分为 [7,2,5] 和 [10,8] 。
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。
解答1:动态规划
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
int n = nums.size();
vector<int> preSum(n + 1); // 前缀和,用于求取区间加和
for (int i = 0; i < n; i++)
preSum[i + 1] = preSum[i] + nums[i];
vector<vector<int>> dp(n + 1, vector<int>(m + 1, INT_MAX)); // 方程中有一个和自己取最小的操作,所以初始化为INT_MAX
dp[0][0] = 0; // dp数组多申请一个长度,就不用讨论边界情况,只需要初始化dp[0][0]
for (int i = 1; i <= n; i++) // 从1出发,还能正好与preSum对齐
for (int j = 1; j <= m; j++)
for (int k = j - 1; k < i; k++) // k小于j-1时没意义,因为j-1堆至少会有j-1个数
dp[i][j] = min(dp[i][j], max(dp[k][j - 1], preSum[i] - preSum[k]));
return dp[n][m];
}
};
重点思路
最大最小值问题,可以使用动态规划进行搜索。类似题目【LeetCode-375】猜数字大小 II。本题中,动态规划设为dp[i][j],表示以i结尾,分为j个子数组时的结果。
解答2:二分查找
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
int left = 0, right = 0, res = 0;
for (int i : nums) {
left = max(left, i);
right += left;
}
while (left < right) {
int mid = left + (right - left) / 2;
if (check(nums, m, mid)) left = mid + 1;
else right = mid;
}
return left;
}
private:
bool check(vector<int>& nums, int m, int mid) {
int cnt = 1, sum = 0;
for (int i : nums) {
sum += i;
if (sum > mid) {
sum = i;
cnt++;
}
}
return cnt > m; // 当前最大值下,需要划分的堆数大于题目要求
}
};
重点思路
二分查找问题都有一个特点,那就是题目要求一个最大最小值问题,也就是在保证能完成某一任务的前提下的最小值。对于这种问题,我们通常会猜测一个值,然后使用这个值去模拟整个操作过程,看最后的结果是否满足要求,再根据这个返回的布尔值决定左右边界的移动。

浙公网安备 33010602011771号