求带启动等待时间、可并行处理4任务的工人最短完工时间(类LeetCode1732)
问题分析与解法思路
首先明确:你提到的LeetCode 1732(找最高海拔)和当前任务分配问题完全无关,应该是笔误——你实际想对比的应该是LeetCode 1723《完成所有工作的最短时间》,这是经典的任务分配求最短完成时间问题,当前问题是它的变种,新增了两个关键约束:
- 每个工人每次最多处理4个任务,一组任务的完成时间为该组任务的最大耗时
- 工人首次开工需要额外等待时间,后续组无需等待
核心问题拆解
我们的目标是最小化所有工人完成时间的最大值(工人并行工作,总完成时间由最晚结束的工人决定),同时满足:
- 所有任务必须全部分配,每个任务仅属于一名工人
- 每名工人的任务被划分为若干组,每组最多4个任务
- 工人的总完成时间 = 首次等待时间 + 所有组的最大耗时之和
解法思路:二分查找+回溯剪枝
这是解决这类“最小化最大完成时间”问题的通用高效思路,具体步骤如下:
1. 二分查找确定候选时间范围
- 左边界left:取两个值的最大值:单个任务的最大耗时(任何组的完成时间都不能小于它)、最小等待时间+单个任务最大耗时(最快可能的首次开工完成时间)
- 右边界right:最坏情况的总时间,比如取
sum(wait) + sum(jobs)(足够宽松,不影响二分效率)
2. 回溯验证候选时间是否可行
对于每个候选时间T,判断能否将所有任务分配给k个工人,使得每个工人的总完成时间不超过T。结合以下优化提升效率:
- 任务降序排序:优先分配耗时大的任务,快速排除不可能的分配方案,减少回溯分支
- 工人状态跟踪:每个工人记录三个状态:
- 已完成的总耗时(已结束的所有组的时间总和,包含首次等待时间)
- 当前正在处理的组的任务数量
- 当前正在处理的组的最大任务耗时
- 剪枝策略:
- 分配任务后总耗时超过
T,直接跳过该分支 - 当前工人和前一个工人状态完全相同,跳过重复计算
- 当前工人未开始工作且分配失败,直接终止后续工人尝试(后续工人等待时间更长,更不可能满足)
- 分配任务后总耗时超过
伪代码实现示例
def min_total_time(jobs, k, wait): jobs.sort(reverse=True) n = len(jobs) def is_feasible(T): # 工人状态:(已完成总耗时, 当前组任务数, 当前组最大耗时) workers = [(0, 0, 0) for _ in range(k)] return backtrack(0) def backtrack(job_idx): if job_idx == n: return True current_job = jobs[job_idx] for i in range(k): total, cnt, curr_max = workers[i] # 情况1:工人还未开始任何任务 if cnt == 0: new_total = wait[i] + current_job if new_total > T: continue workers[i] = (new_total, 1, current_job) if backtrack(job_idx + 1): return True workers[i] = (0, 0, 0) # 第一个工人无法分配,后续工人等待时间更长,直接终止 if i == 0: break else: # 情况2:当前组还能加任务(不足4个) if cnt < 4: new_curr_max = max(curr_max, current_job) workers[i] = (total, cnt + 1, new_curr_max) if backtrack(job_idx + 1): return True workers[i] = (total, cnt, curr_max) if cnt == 0: break # 情况3:当前组已满,新开一组 else: new_total = total + current_job if new_total > T: continue workers[i] = (new_total, 1, current_job) if backtrack(job_idx + 1): return True workers[i] = (total, cnt, curr_max) # 剪枝:避免重复处理相同状态的工人 if i > 0 and workers[i] == workers[i-1]: continue return False # 确定二分边界 left = max(max(jobs), min(wait) + max(jobs)) right = sum(wait) + sum(jobs) while left < right: mid = (left + right) // 2 if is_feasible(mid): right = mid else: left = mid + 1 return left
示例验证
- 示例1:
jobs = [3,3,4,2,9,8,5,6],k=2,wait=[9,11]
任务降序后为[9,8,6,5,4,3,3,2],二分查找得到最小T=18:- 工人1分配
[9,8,4,3],总时间9+9=18 - 工人2分配
[6,5,3,2],总时间11+6=17,满足≤18
- 工人1分配
- 示例2:1名工人,
wait=3,任务[2,1,1,1,1]
无论如何分组,总时间都是3 + max(1,1,1,1) + max(2) = 3+1+2=6,符合预期
内容的提问来源于stack exchange,提问作者user1691278
相关产品推荐
相关产品推荐

