1、问题描述(排列树)

给定n个作业的集合{J1,J2,…,Jn}。每个作业必须先由机器1处理,然后由机器2处理。作业Ji需要机器j的处理时间为tji。对于一个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。所有作业在机器2上完成处理的时间和称为该作业调度的完成时间和,即F12+F22 +F32+…+Fn2。批处理作业调度问题要求对于给定的n个作业,制定最佳作业调度方案,使其完成时间和达到最小。

2、简单描述

对于给定的n个作业,指定最佳作业调度方案,使其完成时间和达到最小。
区别于流水线调度问题:批处理作业调度旨在求出使其完成时间和(第一个最终完成时间+第二个最终完成时间+第三个最终完成时间)达到最小的最佳调度序列;
流水线调度问题旨在求出使其最后一个作业的完成时间最小的最佳调度序列;

3、算法分析

从n个作业中找出有最小完成时间和的作业调度,所以批处理作业调度问题的解空间是一棵排列树。
类Flowshop的数据成员记录解空间的结点信息,以减少传给Backtrack的参数

int n; // 作业数
int f1=0; // 机器1完成处理时间
int f=0; // 完成时间和
int bestf=10000; // 当前最优值
int m[100][100]; // 各作业所需的处理时间,m[j][i]表示在第i台机器上作业j的处理时间
int x[100]; // 当前作业调度
int bestx[100]; // 当前最优作业调度
int f2[100]; // 机器2完成处理时间
初始时,x[i]=i ; bestx[i]=∞; (i=0,1,…,n)

示例

tji 机器1 机器2
作业1 2 1
作业2 3 1
作业3 2 3

调度分析

image.png
处理结束时间:10,“结束时间和”:3+6+10=19

image.png
处理结束时间:8,“结束时间和”:3+7+8=18
……
最优调度顺序:1 3 2
处理时间:18

比较

调度方案 批处理作业调度 流水作业调度
(1, 2, 3) 19 10
(1, 3, 2) 18 8
(2, 1, 3) 20 10
(2, 3, 1) 21 9
(3, 1, 2) 19 8
(3, 2, 1) 19 8

在递归函数Backtrack中:
1、当i>n时,算法搜索至叶子结点,得到一个新的作业调度方案。此时算法适时更新当前最优值和相应的当前最佳调度。
2、当i<=n时,当前扩展结点在i层,以深度优先方式,递归的对相应子树进行搜索,对不满足上界约束的结点,则剪去相应的子树。

时间复杂度

算法backtrack在每个节点处耗费的时间O(1)计算时间,因此在最坏的情况下,整个算法时间复杂度为O(n!)

  1. void backtrack(int i)
  2. {
  3. if (i > n) //到达叶子结点,搜索到最底部
  4. {
  5. for (int j = 1; j <= n; j++)
  6. bestx[j] = x[j];
  7. bestf = f;
  8. }
  9. else
  10. for (int j = i; j <= n; j++)
  11. {
  12. f1+=m[x[j]][1];
  13. f2[i]=((f2[i-1]>f1)?f2[i-1]:f1)+m[x[j]][2];
  14. //上一个任务在机器2上完成的时间是否大于本次任务在1上完成的时间
  15. f+=f2[i];
  16. if (f < bestf)
  17. {
  18. swap(x[i], x[j]);//交换两个作业的位置
  19. backtrack(i+1);
  20. swap(x[i], x[j]);
  21. }
  22. f1-=m[x[j]][1];
  23. f-=f2[i];
  24. }
  25. }
  1. #include<iostream>
  2. using namespace std;
  3. int n; // 作业数
  4. int f1=0; // 机器1完成处理时间
  5. int f=0; // 完成时间和
  6. int bestf=10000; // 当前最优值
  7. int m[100][100]; // 各作业所需的处理时间
  8. int x[100]; // 当前作业调度
  9. int bestx[100]; // 当前最优作业调度
  10. int f2[100]; // 机器2完成处理时间
  11. void backtrack(int i)
  12. {
  13. if (i > n) //到达叶子结点,搜索到最底部
  14. {
  15. if (f < bestf)//存在更优解
  16. {
  17. for (int j = 1; j <= n; j++)
  18. bestx[j] = x[j];
  19. bestf = f;
  20. }
  21. }
  22. else
  23. for (int j = i; j <= n; j++)
  24. {
  25. f1+=m[x[j]][1];
  26. f2[i]=((f2[i-1]>f1)?f2[i-1]:f1)+m[x[j]][2];
  27. //上一个任务在机器2上完成的时间是否大于本次任务在1上完成的时间
  28. f+=f2[i];
  29. if (f < bestf)
  30. {
  31. swap(x[i], x[j]);//交换两个作业的位置
  32. backtrack(i+1);
  33. swap(x[i], x[j]);
  34. }
  35. f1-=m[x[j]][1];
  36. f-=f2[i];
  37. }
  38. }
  39. int main()
  40. {
  41. int i, j;
  42. cout << "请输入作业数:" << endl;
  43. cin >> n;
  44. cout << "请输入在各机器上的处理时间" << endl;
  45. for (i = 1; i <= 2; i++)
  46. for (j = 1; j <= n; j++)
  47. cin >> m[j][i];
  48. for (i = 1; i <= n; i++)
  49. x[i] = i;//初始化
  50. backtrack(1);
  51. cout << "调度作业顺序:" << endl;
  52. for (i = 1; i <= n; i++)
  53. cout << bestx[i] << ' ';
  54. cout << endl;
  55. cout << "处理时间:" << endl;
  56. cout << bestf;
  57. return 0;
  58. }
  59. /*
  60. 测试数据:
  61. 3
  62. 2 3 2
  63. 1 1 3
  64. 3
  65. 2 5 4
  66. 3 2 1
  67. */