- 完成所有工作的最短时间
给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。
请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。
返回分配方案中尽可能 最小 的 最大工作时间 。
参考官方题解的第一个思路
总体思路:最大工作时间的下限是所有工作时间中最大的一个,上限是所有工作的时间和,采用二分法,对上下限之间的值进行判断,若能否成功执行分配任务,上限修改为中间值(此处不宜设置为mid+1,mid+1可能是一个失败的值),若不能,下限修改为中间值+1.
判断能否执行分配任务时使用递归回溯策略。在判断第j项任务时,数组workload存放每一个工人当前已被分配的任务(包括前j-1项),若当前任务分配给i,workload不大于limit,则接着判断j+1能否分配。若所有的任务都可以分配,则说明存在一种分配方案,使得上限为limit成功实现,将结果反馈到main函数,修改上限right值。若不能分配,修改下限left值。
优化:
- 先分配所用时间较长的任务。
- 当前任务分配给i时,若分配失败且i当前的任务量为0,则i之后的工作人员已分配的任务也为0,分配方式和i是一样的,所以此时可以剪枝。
- (未理解)若将j分配给i之后,i的工作量恰好为limit,且后续分配失败,那么不存在一种成功的方案,可以将j分配给i之后的工作人员。
bool traceback(vector<int>&jobs,vector<int>&workload,int index,int limit){
if(index>=jobs.size()){
return true;
}
int cur=jobs[index];
for(auto&x:workload){
if(x+cur<=limit){
x+=cur;
if(traceback(jobs,workload,index+1,limit)){
return true;
}
x-=cur;//分配失败时,一定要把分配给x的任务减掉
}
if(x==0){
break;
}
}
return false;
}
bool check(vector<int>&jobs,int k,int limit){
vector<int>workload(k);
return traceback(jobs,workload,0,limit);
}
int minimumTimeRequired(vector<int>& jobs, int k) {
sort(jobs.begin(),jobs.end(),greater<int>());
int l=jobs[0],r=accumulate(jobs.begin(),jobs.end(),0),mid;
while(l<r){
mid=(l+r)>>1;//位运算求中值
if(check(jobs,k,mid)){
r=mid;//分配成功则修改上限
}else{
l=mid+1;//分配失败则修改下限
}
}
return l;
}