2021.05.06分隔数组的最大值
(题目来源:https://leetcode-cn.com/problems/split-array-largest-sum/)
题目描述
给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。
设计一个算法使得这 m 个子数组各自和的最大值最小。
思路
思路1:动态规划
- 定义状态dp[i][j]: 前i个元素分成j段的最大值
- 状态转移方程
dp[i][j] = Min{ Max{ dp[k][j-1], sum(j,i) }, dp[i][j] }
思路1 代码
int[] sarr;
public int splitArray(int[] nums, int m) {
int n = nums.length;
sarr = new int[n+1];
//dp[i][j]: 长度为i,分成j段,最小的 “最大和”
int[][] dp = new int[n+1][m+1];
for (int i = 0; i <= n; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
dp[0][0] = 0;
for(int i = 0; i < n; i++) {
sarr[i+1] = nums[i]+sarr[i];
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m && j <= i; j++) {
for(int k = j-1; k < i; k++) {
// 0 ~ k 被分成 j-1份, k+1 ~ i 被分成一份
dp[i][j] = Math.min(Math.max(dp[k][j-1],sarr[i]-sarr[k]), dp[i][j]);
}
}
}
return dp[n][m];
}
思路2:二分答案
显然,res的范围在[ max of nums, sum of nums ]之间,我们通过给定res值,看它能分出多少个(设为cnt)数组来,如果cnt > m,则说明猜测的res值太小了,需要在取值的后半段进行猜测。一直猜测,知道区间缩小为1为止。
二分代码:
public int splitArray(int[] nums, int m) {
int s = 0;
int max = Integer.MIN_VALUE;
for(int x: nums) {
s+=x;
max = Math.max(x, max);
}
int low = max, high = s;
while(low < high) {
int mid = (high+low)/2;
int cnt = 1; //此处必须是1,不可为0,与下面t>mid对应
int t = 0;
for(int x: nums) {
t+=x;
if(t > mid) { //此处必须是大于,不能等于
t = x;
cnt++;
}
}
if(cnt > m) {
//如果cnt刚好就是m,那么不改变low的值(其要返回)
low = mid+1;
} else {
high = mid;
}
}
return low;
}