给你一个整数数组 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],0for _, job := range jobs {r+=job}return l+ sort.Search(r-l, func(limit int) bool {limit+=lload := make([]int, k)var bc func(cur int) boolbc = 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)})
}