给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
注意:
数组长度 n 满足以下条件:
1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:
输入:
nums = [7,2,5,10,8]
m = 2
输出:
18
解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。
我的代码 56ms
class Solution {
public:
int splitArray(vector<int>& nums, int m) {
vector<vector<long long>> f(nums.size()+1,vector<long long>(m+1,INT_MAX));
f[0][1]=0;
for(int i=1;i<f.size();i++)
for(int j=1;j<=i&&j<=m;j++)
{
if(j==1) f[i][j]=f[i-1][1]+nums[i-1];
long long last=0;
for(int k=1;k<=i-j+1;k++)
{
last+=nums[i-k];
f[i][j]=min(f[i][j],max(f[i-k][j-1],last));
if(last>=f[i][j]) break;
}
}
return f[nums.size()][m];
}
};
二分法 4ms
/用二分法去猜子数组的容量
//如7 2 5 10 8 假设只有一个子数组那么数组的容量为32 如果有5个子数组那容量就是最大的那个数 10
//所以从[10,32]之间用二分法
//第一次 mid = (10+32)/2=21 我们刚开始假设只有一个子数组,那么need=1, 然后把数字一个一个塞进去
//先塞7 7<21 继续 2 7+2<21 继续 7+2+5<21 继续 7+2+5+10>21 就意味着一个数组放不下我们让need+1=2
//然后把后面的塞完,我们把need和m对比一下
//如果比m大说明我们开的子数组太多,也就意味值我们数组容量太小 子数组数量*容量=数组和(不是很精确,大
//概是这个意思) 所以我们就从[22,32]区间中找
//否则在[10,21]中找
//我们这个例子从[10,21] 最后找到了18
class Solution {
public:
bool can_split(vector<int>&nums,int m,long int medium)
{
int ans=1;
long int sum=0;
for(int i=0;i<nums.size();i++)
{
sum+=nums[i];
if(sum>medium)
{
ans++;
sum=nums[i];
}
}
return (ans>m);
}
int splitArray(vector<int>& nums, int m) {
int n=nums.size();
long int left=0;
long int right=0;
for(int i=0;i<n;i++)
{
right+=nums[i];
left=nums[i]>left?nums[i]:left;
}
while(left<right)
{
long int medium=(left+right)/2;
if(can_split(nums,m,medium))
left=medium+1;
else
right=medium;
}
return left;
}
};