这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战
1723. 完成所有工作的最短时间
给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间。
请你将这些工作分配给 k 位工人。所有工作都应该分配给工人,且每项工作只能分配给一位工人。工人的 工作时间 是完成分配给他们的所有工作花费时间的总和。请你设计一套最佳的工作分配方案,使工人的 最大工作时间 得以 最小化 。
返回分配方案中尽可能 最小 的 最大工作时间 。
示例 1:
输入:jobs = [3,2,3], k = 3 输出:3 解释:给每位工人分配一项工作,最大工作时间是 3 。 示例 2:
输入:jobs = [1,2,4,7,8], k = 2 输出:11 解释:按下述方式分配工作: 1 号工人:1、2、8(工作时间 = 1 + 2 + 8 = 11) 2 号工人:4、7(工作时间 = 4 + 7 = 11) 最大工作时间是 11 。
解题思路
1. 二分
对于最小 的 最大工作时间进行二分搜索,同时限定搜索的区间[时间最长的工作,所有工作的总时间],右边界为所有工作的总时间(一个人干完所有的活),时间最长的工作(即使在最好的情况下,也要花费这个工作的时间)
因为golang的sort.Search函数搜索的区间是[0,n),因此需要修改二分的代码
l+ sort.Search(r-l, func(limit int) bool {
limit+=l
2. 剪枝
if lo+jobs[cur]==limit || load[i]==0{
break
}
两种情况需要剪枝 在将cur号工作分配给i号工人时,递归下去无法找到满足条件的解情况下(即bc(cur+1)为false的情况,如果是true就直接return了)
-
当前的工人要完成这个工作的话,刚刚等于限定的工作时间
-
当前工人没被分配工作(load[i]==0) 当前的工人即使分配了工作的情况下,都不能满足条件了。如果load[i]==0,就是这次不干活了,活就要给其他人干,需要的时间就更长了,那就更不可能完成任务了
3. 回溯
从大到小递归遍历每一项工作时间,在一次递归中尝试将当前工作分配给每个工人,维护一个保存工人工作时间的数组,然后开启层递归下一个工作,如果不满足条件则回溯数组,直到工作都分配完。
代码
func minimumTimeRequired(jobs []int, k int) int {
sort.Sort(sort.Reverse(sort.IntSlice(jobs)))
l,r:=jobs[0],0
for _, job := range jobs {
r+=job
}
return l+ sort.Search(r-l, func(limit int) bool {
limit+=l
load := make([]int, k)
var bc func(cur int) bool
bc = func(cur int) bool{
if cur==len(jobs){
return true
}
for i, lo := range load {
if lo+jobs[cur]<=limit{
load[i]+=jobs[cur]
if bc(cur+1){
return true
}
load[i]-=jobs[cur]
}
if lo+jobs[cur]==limit || load[i]==0{
break
}
}
return false
}
return bc(0)
})
}