1.问题描述
    给定n个作业的集合J={J1,J2,…,Jn}。每一个作业有两项任务分别在两台机器上完成。每个作业必须先由机器1处理,再由机器2处理。作业Ji需要机器j的处理时间为tji,i=1,2,…n,j=1,2。对于一个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。则所有作业在机器2上完成处理的时间和f=F21+F22+…+F2n称为该作业调度的完成时间和。
    批处理作业调度问题要求,对于给定的n个作业,制定最佳的作业调度方案,使其完成时间和最小。
    样例输入:
    3
    2 1
    3 1
    2 3
    样例输出:
    18
    1 3 2
    2. 算法描述
    算法中用活节点优先队列。元素类型是Node。每一个Node类型的节点包含域x,用来表示节点所相应的作业调度。s表示该作业已安排的作业时x[1:s]。f1表示当前已安排的作业在机器1上的最后完成时间;f2表示当前已安排作业在机器2上的完成时间;sf2表示当前已安排的作业在机器2上的完成时间和;bb表示当前完成时间和下界。二维数组M表示所给的n个作业在机器1和机器2所需的处理时间。函数Bound用于计算完成时间和下界。
    函数flowShop中while循环完成对排列树内部结点的有序扩展。在while循环体内算法依次从活结点优先队列中取出具有最小bb值(完成时间和下界)的结点作为当前扩展结点,并加以扩展。算法将当前扩展节点E分两种情形处理:
    1)当E.s=bestc时,可将结点node舍去。
    2)E.s=n的情形,当前节点为最优解。

    1. #include<iostream>
    2. using namespace std;
    3. int n1; // 作业数
    4. int f1 = 0; // 机器1完成处理时间
    5. int f = 0; // 完成时间和
    6. int bestf = 10000; // 当前最优值
    7. int m[100][100]; // 各作业所需的处理时间
    8. int x[100]; // 当前作业调度
    9. int best_x[100]; // 当前最优作业调度
    10. int f2[100]; // 机器2完成处理时间
    11. void backtrack(int i)
    12. {
    13. if (i > n1 && f < bestf) //到达叶子结点,搜索到最底部且存在最优解
    14. {
    15. for (int j = 1; j <= n1; j++)
    16. best_x[j] = x[j];
    17. bestf = f;
    18. }
    19. else
    20. for (int j = i; j <= n1; j++)
    21. {
    22. f1 += m[x[j]][1];
    23. f2[i] = ((f2[i - 1] > f1) ? f2[i - 1] : f1) + m[x[j]][2]; //上一个任务在机器2上完成的时间是否大于本次任务在1上完成的时间
    24. f += f2[i];
    25. if (f < bestf)
    26. {
    27. swap(x[i], x[j]);//交换两个作业的位置
    28. backtrack(i + 1);
    29. swap(x[i], x[j]);
    30. }
    31. f1 -= m[x[j]][1];
    32. f -= f2[i];
    33. }
    34. }
    35. int main()
    36. {
    37. cout << "请输入作业数:";
    38. cin >> n1;
    39. cout << "请输入在各机器上的处理时间" << endl;
    40. for (int i = 1; i <= n1; i++)
    41. for (int j = 1; j <= 2; j++)
    42. cin >> m[i][j];
    43. for (int i = 1; i <= n1; i++)
    44. x[i] = i; //初始化
    45. backtrack(1);
    46. cout << "最少完成时间: " << bestf << endl << "最优调度: ";
    47. for (int i = 1; i <= n1; i++)
    48. cout << best_x[i] << " ";
    49. cout << endl;
    50. return 0;
    51. }
    1. #include<iostream>
    2. #include<queue>
    3. using namespace std;
    4. struct Node
    5. {
    6. int s; //已安排作业数
    7. int f1; //机器1上最后完成时间
    8. int f2; //机器2上最后完成时间
    9. int sf2; //当前机器2完成时间和
    10. int* x; //当前作业调度
    11. };
    12. const int MAX = 100;
    13. const int MACHINE = 2;
    14. int n; //作业数
    15. int M[MAX][MACHINE]; //各作业所需时间,键盘输入
    16. int b[MAX][MACHINE]; //各作业所需时间排序后
    17. int a[MAX][MACHINE]; //数组m和b的对应关系
    18. bool y[MAX][MACHINE]; //工作数组
    19. int bestx[MAX]; //最优解
    20. int bestc = 1e6; //最小完成时间和
    21. queue<Node> q; //队列
    22. //最小堆节点初始化
    23. Node Init()
    24. {
    25. Node node;
    26. node.x = new int[n];
    27. for (int i = 0; i < n; i++)
    28. node.x[i] = i;
    29. node.s = 0;
    30. node.f1 = 0;
    31. node.f2 = 0;
    32. node.sf2 = 0;
    33. return node;
    34. }
    35. //最小堆新节点
    36. Node NewNode(Node E, int Ef1, int Ef2)
    37. {
    38. Node node = Init();
    39. node.x = new int[n];
    40. for (int i = 0; i < n; i++)
    41. node.x[i] = E.x[i];
    42. node.f1 = Ef1;
    43. node.f2 = Ef2;
    44. node.sf2 = E.sf2 + Ef2; //E為父結點,計算子結點当前机器2完成时间和
    45. node.s = E.s + 1;
    46. return node;
    47. }
    48. void sort() {
    49. int* c = new int[n];
    50. for (int j = 0; j < 2; j++) //对机器1和机器2别排序,并在a数组中记录作业序号
    51. {
    52. for (int i = 0; i < n; i++)
    53. {
    54. b[i][j] = M[i][j];
    55. c[i] = i;
    56. }
    57. for (int i = 0; i < n - 1; i++)
    58. for (int k = n - 1; k > i; k--)
    59. if (b[k][j] < b[k - 1][j])
    60. {
    61. swap(b[k][j], b[k - 1][j]);
    62. swap(c[k], c[k - 1]);
    63. }
    64. for (int i = 0; i < n; i++)
    65. a[c[i]][j] = i;
    66. }
    67. delete[]c;
    68. }
    69. //排序结果:
    70. /*b[][]
    71. 2 - 1
    72. 2 - 1
    73. 3 - 3
    74. */
    75. /*a[][]
    76. 0 * 0
    77. 2 * 1
    78. 1 * 2
    79. */
    80. //计算完成时间下界
    81. int Bound(Node E, int& f1, int& f2)
    82. {
    83. for (int k = 0; k < n; k++)
    84. for (int j = 0; j < 2; j++)
    85. y[k][j] = false;
    86. for (int k = 0; k <= E.s; k++)
    87. for (int j = 0; j < 2; j++)
    88. y[a[E.x[k]][j]][j] = true;
    89. f1 = E.f1 + M[E.x[E.s]][0];
    90. f2 = ((f1 > E.f2) ? f1 : E.f2) + M[E.x[E.s]][1];
    91. int sf2 = E.sf2 + f2;
    92. int s1 = 0, s2 = 0;
    93. int k1 = n - E.s, k2 = n - E.s;
    94. int f3 = f2;
    95. for (int j = 0; j < n; j++) //计算s1
    96. if (!y[j][0])
    97. {
    98. k1--;
    99. if (k1 == n - E.s - 1)
    100. f3 = (f2 > f1 + b[j][0]) ? f2 : f1 + b[j][0];
    101. s1 += f1 + k1 * b[j][0];
    102. }
    103. for (int j = 0; j < n; j++) //计算s2
    104. if (!y[j][1])
    105. {
    106. k2--;
    107. s1 += b[j][1];
    108. s2 += f3 + k2 * b[j][1];
    109. }
    110. return sf2 + (s1 > s2 ? s1 : s2); //返回完成计算的时间下界
    111. }
    112. void flowShop()
    113. {
    114. sort(); //机器1和机器2上所需时间排序
    115. Node E = Init();
    116. while (E.s <= n) //搜索排列树
    117. {
    118. if (E.s == n && E.sf2 < bestc) //叶节点且存在最优解——>更新
    119. {
    120. bestc = E.sf2;
    121. for (int i = 0; i < n; i++)
    122. bestx[i] = E.x[i];
    123. delete[]E.x;
    124. }
    125. else
    126. { //产生扩展当前节点的儿子节点
    127. for (int i = E.s; i < n; i++)
    128. {
    129. int f1, f2; //計算机器1、机器2上最后完成时间
    130. swap(E.x[E.s], E.x[i]);
    131. int bb = Bound(E, f1, f2);
    132. if (bb < bestc) //子树可能含有最优解
    133. q.push(NewNode(E, f1, f2));
    134. swap(E.x[E.s], E.x[i]);
    135. }
    136. delete[]E.x;
    137. } //完成节点扩展
    138. if (q.empty())
    139. break;
    140. E = q.front();
    141. q.pop();
    142. }
    143. }
    144. void BFS() {
    145. cout << "请输入作业数:";
    146. cin >> n;
    147. cout << "输入作业所需时间:";
    148. for (int i = 0; i < n; i++) /*初始化*/
    149. for (int j = 0; j < 2; j++)
    150. cin >> M[i][j];
    151. flowShop();
    152. cout << "最少完成时间: " << bestc << endl << "最优调度: ";
    153. for (int i = 0; i < n; i++)
    154. cout << bestx[i] + 1 << " ";
    155. cout << endl;
    156. }
    157. int main()
    158. {
    159. BFS(); //分支限界
    160. //DFS(); //回溯
    161. return 0;
    162. }
    163. /*
    164. * 測試1:
    165. * 请输入作业数:3
    166. * 输入作业所需时间:2 1 3 1 2 3
    167. * 測試2:
    168. * 请输入作业数:3
    169. * 输入作业所需时间:2 3 5 2 4 1
    170. */
    #include<iostream>
    #include<queue>
    using namespace std;
    
    struct Node
    {
        int s;          //已安排作业数
        int f1;         //机器1上最后完成时间
        int f2;         //机器2上最后完成时间
        int sf2;        //当前机器2完成时间和
        int bb;         //当前完成时间下界
        int* x;         //当前作业调度
        bool operator < (const Node& node) const {
            return bb > node.bb;
            //return sf2 > node.sf2;            //效率低
        }
    };
    
    const int MAX = 100;
    const int MACHINE = 2;
    int n;                    //作业数
    int M[MAX][MACHINE];      //各作业所需时间,键盘输入
    int b[MAX][MACHINE];      //各作业所需时间排序后
    int a[MAX][MACHINE];      //数组m和b的对应关系
    bool y[MAX][MACHINE];     //工作数组
    int bestx[MAX];           //最优解
    int bestc = 1e6;          //最小完成时间和
    
    priority_queue<Node> q;    //优先队列
    
    //最小堆节点初始化
    Node Init()
    {
        Node node;
        node.x = new int[n];
        for (int i = 0; i < n; i++)
            node.x[i] = i;
        node.s = 0;
        node.f1 = 0;
        node.f2 = 0;
        node.sf2 = 0;
        node.bb = 0;
        return node;
    }
    
    //最小堆新节点
    Node NewNode(Node E, int Ef1, int Ef2, int Ebb)
    {
        Node node;
        node.x = new int[n];
        for (int i = 0; i < n; i++)
            node.x[i] = E.x[i];
        node.f1 = Ef1;
        node.f2 = Ef2;
        node.sf2 = E.sf2 + Ef2;
        node.bb = Ebb;
        node.s = E.s + 1;
        return node;
    }
    
    void sort() {
        int* c = new int[n];
        for (int j = 0; j < 2; j++)
        {
            for (int i = 0; i < n; i++)
            {
                b[i][j] = M[i][j];
                c[i] = i;
            }
            for (int i = 0; i < n - 1; i++)
                for (int k = n - 1; k > i; k--)
                    if (b[k][j] < b[k - 1][j])
                    {
                        swap(b[k][j], b[k - 1][j]);
                        swap(c[k], c[k - 1]);
                    }
            for (int i = 0; i < n; i++)
                a[c[i]][j] = i;
        }
        delete[]c;
    }
    
    //计算完成时间上界
    int Bound(Node E, int& f1, int& f2)
    {
        for (int k = 0; k < n; k++)
            for (int j = 0; j < 2; j++)
                y[k][j] = false;
        for (int k = 0; k <= E.s; k++)
            for (int j = 0; j < 2; j++)
                y[a[E.x[k]][j]][j] = true;
    
        f1 = E.f1 + M[E.x[E.s]][0];
        f2 = ((f1 > E.f2) ? f1 : E.f2) + M[E.x[E.s]][1];
        int sf2 = E.sf2 + f2;
        int s1 = 0, s2 = 0;
        int k1 = n - E.s, k2 = n - E.s;
        int f3 = f2;
        for (int j = 0; j < n; j++)             //计算s1
            if (!y[j][0])
            {
                k1--;
                if (k1 == n - E.s - 1)
                    f3 = (f2 > f1 + b[j][0]) ? f2 : f1 + b[j][0];
                s1 += f1 + k1 * b[j][0];
            }
        for (int j = 0; j < n; j++)             //计算s2
            if (!y[j][1])
            {
                k2--;
                s1 += b[j][1];
                s2 += f3 + k2 * b[j][1];
            }
        return sf2 + (s1 > s2 ? s1 : s2);       //返回完成计算的时间下界
    }
    void flowShop()
    {
        sort();                                 //机器1和机器2上所需时间排序
    
        Node E = Init();
    
        while (E.s < n)                         //搜索排列数
        {                                       //产生扩展当前节点的儿子节点
            for (int i = E.s; i < n; i++)
            {
                int f1, f2;
                swap(E.x[E.s], E.x[i]);
                int bb = Bound(E, f1, f2);
                if (bb < bestc)             //子树可能含有最优解
                    q.push(NewNode(E, f1, f2, bb));
                swap(E.x[E.s], E.x[i]);
            }
            delete[]E.x;                    //完成节点扩展
    
            if (q.empty())
                break;                
            E = q.top();
           /* cout << E.bb << "   " << E.sf2 << endl;*/
            q.pop();
            if (E.s == n)      //叶节点且是最优解
            {
                bestc = E.sf2;
                for (int i = 0; i < n; i++)
                    bestx[i] = E.x[i];
                delete[]E.x;
            }
        }
        while (!q.empty()) {
            E = q.top();
            q.pop();
            delete[] E.x;
        }           //清空队列            
    }
    
    int main(){
        cout << "请输入作业数:";
        cin >> n;
        cout << "输入作业所需时间:";
        for (int i = 0; i < n; i++)         /*初始化*/
            for (int j = 0; j < 2; j++)
                cin >> M[i][j];
    
        flowShop();
    
        cout << "最少完成时间: " << bestc << endl << "最有调度: ";
        for (int i = 0; i < n; i++)
            cout << bestx[i] + 1 << " ";
        cout << endl;
        return 0;
    }