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
2)E.s=n的情形,当前节点为最优解。
#include<iostream>
using namespace std;
int n1; // 作业数
int f1 = 0; // 机器1完成处理时间
int f = 0; // 完成时间和
int bestf = 10000; // 当前最优值
int m[100][100]; // 各作业所需的处理时间
int x[100]; // 当前作业调度
int best_x[100]; // 当前最优作业调度
int f2[100]; // 机器2完成处理时间
void backtrack(int i)
{
if (i > n1 && f < bestf) //到达叶子结点,搜索到最底部且存在最优解
{
for (int j = 1; j <= n1; j++)
best_x[j] = x[j];
bestf = f;
}
else
for (int j = i; j <= n1; j++)
{
f1 += m[x[j]][1];
f2[i] = ((f2[i - 1] > f1) ? f2[i - 1] : f1) + m[x[j]][2]; //上一个任务在机器2上完成的时间是否大于本次任务在1上完成的时间
f += f2[i];
if (f < bestf)
{
swap(x[i], x[j]);//交换两个作业的位置
backtrack(i + 1);
swap(x[i], x[j]);
}
f1 -= m[x[j]][1];
f -= f2[i];
}
}
int main()
{
cout << "请输入作业数:";
cin >> n1;
cout << "请输入在各机器上的处理时间" << endl;
for (int i = 1; i <= n1; i++)
for (int j = 1; j <= 2; j++)
cin >> m[i][j];
for (int i = 1; i <= n1; i++)
x[i] = i; //初始化
backtrack(1);
cout << "最少完成时间: " << bestf << endl << "最优调度: ";
for (int i = 1; i <= n1; i++)
cout << best_x[i] << " ";
cout << endl;
return 0;
}
#include<iostream>
#include<queue>
using namespace std;
struct Node
{
int s; //已安排作业数
int f1; //机器1上最后完成时间
int f2; //机器2上最后完成时间
int sf2; //当前机器2完成时间和
int* x; //当前作业调度
};
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; //最小完成时间和
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;
return node;
}
//最小堆新节点
Node NewNode(Node E, int Ef1, int Ef2)
{
Node node = Init();
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; //E為父結點,計算子結點当前机器2完成时间和
node.s = E.s + 1;
return node;
}
void sort() {
int* c = new int[n];
for (int j = 0; j < 2; j++) //对机器1和机器2别排序,并在a数组中记录作业序号
{
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;
}
//排序结果:
/*b[][]
2 - 1
2 - 1
3 - 3
*/
/*a[][]
0 * 0
2 * 1
1 * 2
*/
//计算完成时间下界
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) //搜索排列树
{
if (E.s == n && E.sf2 < bestc) //叶节点且存在最优解——>更新
{
bestc = E.sf2;
for (int i = 0; i < n; i++)
bestx[i] = E.x[i];
delete[]E.x;
}
else
{ //产生扩展当前节点的儿子节点
for (int i = E.s; i < n; i++)
{
int f1, f2; //計算机器1、机器2上最后完成时间
swap(E.x[E.s], E.x[i]);
int bb = Bound(E, f1, f2);
if (bb < bestc) //子树可能含有最优解
q.push(NewNode(E, f1, f2));
swap(E.x[E.s], E.x[i]);
}
delete[]E.x;
} //完成节点扩展
if (q.empty())
break;
E = q.front();
q.pop();
}
}
void BFS() {
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;
}
int main()
{
BFS(); //分支限界
//DFS(); //回溯
return 0;
}
/*
* 測試1:
* 请输入作业数:3
* 输入作业所需时间:2 1 3 1 2 3
* 測試2:
* 请输入作业数:3
* 输入作业所需时间:2 3 5 2 4 1
*/
#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;
}