目录
一、问题解析
二、实例剖析
三、算法思路
四、代码实现
结果:
总结
前言
【问题】n 个作业{1, 2, …, n}要在两台机器上处理,每个作业必须先由机器 1 处理,再由机器 2 处理,机器 1 处理作业i所需时间为 ai,机器 2 处理作业 i 所需时间为 bi(1 ≤ i ≤ n),批处理作业调度问题(batch-job scheduling problem)要求确定这 n 个作业的最优处理顺序,使得从第 1 个作业在机器 1 上处理开始,到最后一个作业在机器 2 上处理结束所需时间最少。
一、问题解析
【想法】显然,批处理作业的一个最优调度应使机器 1 没有空闲时间,且机器 2的空闲时间最小。
- 存在一个最优作业调度使得在机器 1 和机器 2 上作业以相同次序完成。
- 由于是一种作业调度顺序的方案,因此该问题的解空间树是排列树。
二、实例剖析
例如,有三个作业{1, 2, 3},在机器 1 上所需的处理时间为(2, 5, 4),在机器 2上所需的处理时间为(3, 2, 1),则存在 6 种可能的调度方案:{(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)},相应的完成时间为{12, 13, 12, 14, 13, 16},最佳调度方案是(1, 2, 3)和(2, 1, 3),最短完成时间为 12。
其他情况如上类似,这里就不再赘述!!
三、算法思路
【算法】设数组a[n]存储 n 个作业在机器 1 上的处理时间,b[n]存储 n 个作业在机器 2 上的处理时间。设数组x[n]表示 n 个作业批处理的一种调度方案,其中x[i]表示第 i 个处理的作业编号,sum1[n]和sum2[n]保存在调度过程中机器 1 和机器 2的当前完成时间,其中sum1[i]表示在安排第 i 个作业后机器 1 的当前完成时间,sum2[i]表示在安排第 i 个作业后机器 2 的当前完成时间,且根据下式进行更新:
关键点:
⭐sum1[i] = sum1[i-1] + a[ x[i] ]
⭐sum2[i] = max(sum1[i], sum2[i-1]) +b[ x[i] ]
算法:回溯法求解批处理调度BatchJob
输入:n 个作业在机器 1 上的处理时间 a[n],在机器 2 上的处理时间 b[n]
输出:最短完成时间bestTime,最优调度序列 x[n]
1. 初始化解向量 x[n] = {-1};最短完成时间bestTime = MAX;
2. 初始化调度方案中机器 1 和机器 2 的完成时间:
sum1[n] = sum2[n] = {0}; i = 0;
3. 当 i >= 0 时调度第 i 个作业:
3.1 依次考察每一个作业,如果作业 x[i] 尚未处理,则转步骤 3.2,
否则尝试下一个作业,即 x[i]++;
3.2 处理作业 x[i]:
3.2.1 sum1[i]=sum1[i-1]+ a[x[i]];
3.2.2 sum2[i]=max{sum1[i], sum2[i-1]}+ b[x[i]];
四、代码实现
#include <iostream>
#include <vector>
#include <climits>using namespace std;int bestTime = INT_MAX;
vector<int> bestSchedule;
vector<int> currentSchedule;void backtrack(vector<int>& a, vector<int>& b, vector<bool>& processed, vector<int>& sum1, vector<int>& sum2, int index, int n) {if (index == n) {if (sum2[n-1] < bestTime) {bestTime = sum2[n-1];bestSchedule = currentSchedule;}return;}for (int i = 0; i < n; i++) {if (!processed[i]) {processed[i] = true;currentSchedule[index] = i;sum1[index] = (index > 0 ? sum1[index-1] : 0) + a[i];sum2[index] = max(sum1[index], (index > 0 ? sum2[index-1] : 0)) + b[i];backtrack(a, b, processed, sum1, sum2, index + 1, n);processed[i] = false;}}
}int batchJobScheduling(vector<int>& a, vector<int>& b) {int n = a.size();vector<bool> processed(n, false);vector<int> sum1(n, 0);vector<int> sum2(n, 0);currentSchedule.resize(n);backtrack(a, b, processed, sum1, sum2, 0, n);return bestTime;
}int main() {vector<int> a = {2, 3, 1};vector<int> b = {3, 1, 2};int result = batchJobScheduling(a, b);cout << "Best processing time: " << result << endl;cout << "Best schedule: ";for (int job : bestSchedule) {cout << job << " ";}cout << endl;return 0;
}
结果:
第一行表示:所需的最短时间;第二行表示:最好的安排次序
总结
算法时间复杂度高,时间消耗太多了,在ACM上一直会超时,不如贪心法效率高。我是一个小菜鸡,欢迎各路大神批评指正!!