问题描述
给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。
设计一个算法使得这 m 个子数组各自和的最大值最小。
思路
最大值最小,典型的二分,关键在于如何写check函数
假设x是最大值的最小值,首先先不考虑最小值这个后缀,
即x至少要满足可以将数组连续分成m个和不超过x的连续子数组
根据这句话我们有两种判断方法
- 1.把数组分成全部小于x的连续子数组最少可以分成cnt组(即每一组都尽量恰好不超过x的分),则cnt必须满足m>=cnt
- 2.把数组分成m份连续数组,一定存在某种分组,使得每份的和都<=x,否则则说明无法分成全部都<=x的m份连续子数组,说明任何一种分法每一份的和都>x,说明当前猜的x小了
代码
class Solution {
public:
int n;
bool check(vector<int>& nums, int x,int m){
int sum=0,cnt=1; //注意cnt初始化为1而不是0,因为最后一次没有加进去
for(int i=0;i<n;i++){
if(sum+nums[i]>x){ // cnt是可以分配为<=x的块的最小块数
sum=nums[i];
cnt++;
}
else sum+=nums[i];
}
return m>=cnt;
}
int splitArray(vector<int>& nums, int m) {
n=nums.size();
int l=0,r=0;
for(int i=0;i<n;i++){
r+=nums[i];
l=max(l,nums[i]);
}
while(l<=r){
int mid=(l+r)>>1;
if(check(nums,mid,m)){
r=mid-1;
}
else l=mid+1;
}
return r+1;
}
};
扩展
- 思路2.把数组分成m份数组,一定存在某种分组,使得每份的和都<=x,否则则说明无法分成全部都<=x的m份子数组,说明任何一种分法每一份的和都>x,说明当前猜的x小了
思路2对应的题目:
完成所有工作的最短时间
给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。
请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。
返回分配方案中尽可能 最小 的 最大工作时间 。
代码
class Solution {
public:
int n;
bool check(vector<int>& jobs, vector<int>& works,int start,int x){
if(start>=n) return true;
int cur=jobs[start]; //现在要分配的工作
for(auto& labor:works){
if(labor+cur<=x){
labor+=cur;
if(check(jobs,works,start+1,x)) return true;
labor-=cur; // 记得回溯要复原
}
if(labor==0||labor+cur==x) break;
}
return false;
}
int minimumTimeRequired(vector<int>& jobs, int k) {
n=jobs.size();
int l=0,r=0;
for(int i=0;i<n;i++){
r+=jobs[i];
l=max(l,jobs[i]);
}
while(l<=r){
int mid=(l+r)>>1;
vector<int> works(k); // k个工人每个人的工作量
if(check(jobs,works,0,mid)){ //检查从工作0开始分配给k个工人,且每个工人工作总量不超过mid,是否能将0之后的工作分配完
r=mid-1;
}
else l=mid+1;
}
return r+1;
}
};