最佳调度问题
LeetCode参考:https://leetcode.cn/problems/find-minimum-time-to-finish-all-jobs/description/
算法设计思路
回溯法:深度优先搜索 + 剪枝。
将问题抽象为一棵深度为 n n n 的 k k k 叉树。树中结点的关键字即为完成任务所需的时间 t i t_i ti。
首先,我们选择个分支一直往下走,以DFS来搜索整个解空间。每次搜索到达叶结点都判断是否是最优路径。若是,更新最优时间bestTime和与之对应的最优调度bestSchedule。一开始我们需要完整的走完一条路径,后面再进行遍历的时候只需要与现有的数据进行比较,当我们发现当前机器的运行时间已经超过当前的最优解时便停止遍历,即进行剪枝操作。以tasks=[3, 2, 3],k=3为例,算法得部分运行步骤如下所示:
源码
class Solution {
private int bestTime = Integer.MAX_VALUE;
public int minimumTimeRequired(int[] jobs, int k) {
int[] machineTime = new int[k];
backtrack(0, 0, jobs.length, k, jobs, machineTime);
return bestTime;
}
public void backtrack(int taskIndex, int curTime, int n, int k, int[] tasks, int[] machineTime) {
if (taskIndex == n) { // 遍历到叶结点
if (curTime < bestTime) { // 如果当前花费的时间小于之前的最优时间
bestTime = curTime; // 更新最优时间
}
return;
}
for (int machine = 0; machine < k; machine++) {
if (machineTime[machine] + tasks[taskIndex] >= bestTime) { // 剪枝,加上完成当前任务的时间大于最优时间,不需要进入递归
continue;
}
if (machine > 0 && machineTime[machine] == machineTime[machine-1]) { // 剪枝,如果和之前的相同,不需要进入递归
continue;
}
machineTime[machine] += tasks[taskIndex]; // 加上花费的时间
if (Math.max(curTime, machineTime[machine]) < bestTime) {
backtrack(taskIndex + 1, Math.max(curTime, machineTime[machine]), n, k, tasks, machineTime);
}
machineTime[machine] -= tasks[taskIndex]; // 回溯
}
}
}