410. 分割数组的最大值
给定一个非负整数数组 nums和一个整数 m ,你需要将这个数组分成 m个非空的连续子数组。
设计一个算法使得这 m个子数组各自和的最大值最小。
示例 1:
输入:nums = [7,2,5,10,8], m = 2
输出:18
解释:
一共有四种方法将 nums 分割为 2 个子数组。 其中最好的方式是将其分为 [7,2,5] 和 [10,8] 。
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。示例 2:
输入:nums = [1,2,3,4,5], m = 2
输出:9示例3
输入:nums = [1,4,4], m = 3
输出:4
提示:
- 1 <= nums.length <= 1000
- 0 <= nums[i] <= 1 0 6 10^6 106
- 1 <= m <= min(50, nums.length)
方法1:前缀和+动态规划
//DP[i][j]表示将数组前i个整数,划分j个子数组,得到的最小最大值
//状态转移方程:DP[i][j]=min(DP[i][j],max(DP[t][j-1],nums[t]+nums[t+1]+...+nums[i-1]))
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
int len=nums.size();
long long INF=9999999999;
vector<long long> presum(len+1,0);
vector<vector<long long>> DP(len+1,vector<long long>(m+1,INF));
for(int i=1;i<=len;i++)
presum[i]=presum[i-1]+nums[i-1];
DP[0][0]=0;
for(int j=1;j<=m;j++){
for(int i=j;i<=len;i++){
for(int t=j-1;t<i;t++){
DP[i][j]=min(DP[i][j],max(DP[t][j-1],presum[i]-presum[t]));
}
}
}
return DP[len][m];
}
};
方法2:二分查找
//将求解极小极大值问题转化为判定问题:给定预设的最大值Max,是否能在最大值约束下划分m个子数组
class Solution {
public:
bool check_groupcnt(vector<int>& nums, int Max, int m){
int sum=0,cnt=0;
for(int i=0;i<nums.size();i++){
if(nums[i]>Max) return false;
sum = sum + nums[i];
if (sum>Max){
cnt++;
if(cnt>m) return false;
sum = nums[i];
}
}
cnt++;
return cnt<=m;
}
int splitArray(vector<int>& nums, int m) {
int L=0,R=1000000000;
while(L<R){
int mid=L+(R-L)/2;
if(check_groupcnt(nums,mid,m)){
R=mid;
}
else{
L=mid+1;
}
}
return L;
}
};
记录此博客的原因:第一次写这道题的时候,直接用动态规划来写,程序通过了,就没有看题解。后来在字节跳动面试上遇到类似的题目,条件稍有修改,不能用动态规划来解,只能用二分法求解,面试时我只能发呆,面试就没通过。。。
这种极大极小化的问题最好用二分法求解
相似题目:1482. 制作 m 束花所需的最少天数