tji 机器1 机器2
作业1 2 1
作业2 3 1
作业3 2 3
作业1在机器1上完成的时间为2,在机器2上完成的时间为3
作业3在机器1上完成的时间为4,在机器2上完成的时间为7
作业2在机器1上完成的时间为7,在机器2上完成的时间为8
3+7+8=18,所以时间和为18
一般情况下,对1个已经安排的作业集合M,|M|=k,M为{1,2,..,n}的子集,设sum1[k]表示机器1完成k个作业的处理时间, sum2[k]表示机器2完成k个作业的处理时间,现在需要处理作业k+1,此时该部分解的目标函数的下界为:
当第k+1步恰好调度第k+1个作业时,
(1) sum1[k+1] = sum1[k] + tk+1,1
sum2[k+1] = max{sum1[k+1], sum2[k]} +tk+1,2
(2) 如果第k个作业在机器1上完成处理后能立即在机器2上开始处理,机器1无空闲时间:
剩下的作业中,第i个作业完成需要的时间为
fi2 = sum1[k] /* i不在M中 */
+ ∑tj1 /* j不在M中,且位于第k个作业之后,i个作业之前, 即 k < j <= i */
+ ti,2
剩余作业完成时间和:
S1 = ∑fi2
= ∑{ sum1[k] + (n-r+1)tr,1 + tr,2 } // k+1 =< r <=n
(3) 如果机器2无空闲时间:
剩下的作业中,第i个作业完成需要的时间为
fi2 = max{ sum2[k], sum1[k]+min{ti,1} } /* i不在M中 */
+ ∑tj,2 /* j不在M中,且位于第k个作业之后,i个作业之前, 即 k < j <= i */
剩余作业完成时间和:
S2 = ∑fi2
= ∑{ max{sum2[k], sum1[k]+min{ti,1}} + (n-r+1)tr,2 } // i不在M中, k+1 =< r <=n
(4) 注意到如果选择r,使tr1在r>=k+1时依非减序排列,S1则取得极小值s1。同理如果选择r使tr2依非减序排列,则 S2取得极小值s2。
(5) 所有作业完成时间和下限:
lb = ∑fm2 + max(s1, s2) /* m在M中,fm2为第m个作业的完成时间 */
#include "cstdio"
#include "queue"
#define MAX 10
#define MACHINE 2
using namespace std;
int n; //作业数
int M[MAX][MACHINE]; //各作业所需的处理时间数组
int b[MAX][MACHINE]; //各作业所需的处理时间排序数组
int a[MAX][MACHINE]; //数组M和b的对应关系数组
int bestx[MAX]; //最优解
int bestc; //最小完成时间和
int y[MAX][MACHINE]; //工作数组
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;
}
};
priority_queue<Node> pq;
//n为作业个数
void initNode(Node &node, int n)
{
node.x = new int[n];
int i;
for(i=0; i<n; i++)
node.x[i] = i;
node.s = 0; //已安排作业数
node.f1 = 0; //机器1上最后完成的时间
node.f2 = 0; //机器2上最后完成的时间
node.sf2 = 0; //当前机器2上的完成时间和
node.bb = 0; //当前完成时间下界
}
//最小堆新结点
void newNode(Node &node, Node E, int Ef1, int Ef2, int Ebb, int n)
{
node.x = new int[n];
int i;
for(i=0; i<n; i++)
node.x[i] = E.x[i];
node.f1 = Ef1; //机器1上最后完成的时间
node.f2 = Ef2; //机器2上最后完成的时间
node.sf2 = E.sf2 + Ef2; //当前机器2上的完成时间和
node.bb = Ebb; //当前完成时间下界
node.s = E.s + 1; //已安排作业数
}
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
//对各作业在机器1和机器2上所需时间冒泡排序
void sort()
{
int *c = new int[n];
int i, j, k;
for(j=0; j<2; j++)
{
for(i=0; i<n; i++)
{
b[i][j] = M[i][j];
c[i] = i;
}
for(i=0; i<n-1; i++)
for(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(i=0; i<n; i++)
a[c[i]][j] = i;
//排序结束后,机器1上运行时间第i小的作业是a[c[i]][0] (i从0开始)
//即机器1上作业运行时间小到大排序为a[c[0]][0], a[c[1]][0], a[c[2]][0]...
// 机器2上作业运行时间小到大排序为a[c[0]][1], a[c[1]][1], a[c[2]][1]...
}
delete []c;
}
//计算完成时间和下界
int bound(Node E, int &f1, int &f2, int y[MAX][MACHINE])
{
int k, j;
for(k=0; k<n; k++)
for(j=0; j<2; j++)
y[k][j] = 0;
//标记包含于已安排作业集合M中中的作业
//即作业E.s及之前的作业
for(k=0; k<=E.s; k++)
for(j=0; j<2; j++)
y[a[E.x[k]][j]][j] = 1;
f1 = E.f1 + M[E.x[E.s]][0]; //sum1[k+1] = sum1[k] + tk+1,1
f2 = ((f1>E.f2)?f1:E.f2) + M[E.x[E.s]][1]; //sum2[k+1] = max{sum1[k+1], sum2[k]} +tk+1,2
int sf2 = E.sf2 + f2;
int s1 = 0, s2 = 0;
int k1 = n - E.s, k2 = n - E.s; //不包含于已安排作业集合M中作业的个数
int f3 = f2;
//计算s1值
//如果第k个作业在机器1上完成处理后能立即在机器2上开始处理,机器1无空闲时间
//s1 = ∑{ sum1[k] + (n-r+1)tr,1 + tr,2 } // k+1 =< r <=n
for(j=0; j<n; j++)
if(!y[j][0]) //不包含于已安排作业集合M中
{
k1--;
if(k1 == n-E.s-1)
f3 = (f2>f1+b[j][0])?f2:f1+b[j][0];
s1 += f1 + k1 * b[j][0];
}
//计算s2值
//如果机器2无空闲时间
//s2 = ∑{ max{sum2[k], sum1[k]+min{ti,1}} + (n-r+1)tr,2 } // i不在M中, k+1 =< r
for(j=0; j<n; j++)
if(!y[j][1])
{
k2--;
s1 += b[j][1];
s2 += f3 + k2 * b[j][1];
}
//∑fm2 + max(s1, s2) /* m在M中,fm2为第m个作业的完成时间 */
return sf2 + (s1>s2?s1:s2);
}
int flowShop()
{
//解批处理作业调度问题的优先队列分支界限法
sort(); //对各作业在机器1和机器2上所需时间的排序
Node E;
initNode(E, n); //初始化
bestc = 1000000;
//搜索排列空间树
while(E.s<=n)
{
if(E.s==n) //叶结点
{
if(E.sf2 < bestc){ //更新最优值
bestc = E.sf2;
int i;
for(i=0; i<n; i++)
bestx[i] = E.x[i];
delete []E.x;
}
}
else //扩展当前结点
{
int i;
for(i=E.s; i<n; i++)
{
swap(E.x[E.s], E.x[i]);
int f1, f2;
int bb = bound(E, f1, f2, y);
if(bb<bestc) //如果可能产生最优解,结点入队列
{
Node node;
newNode(node, E, f1, f2, bb, n);
pq.push(node);
}
swap(E.x[E.s], E.x[i]);
}
delete []E.x; //完成结点扩展
}
//取下一个结点
if(pq.empty())
break;
E = pq.top();
pq.pop();
}
return bestc;
}
void init(int n1, int M1[3][2])
{
n = n1;
int i, j;
for(i=0; i<n; i++)
for(j=0; j<2; j++)
M[i][j] = M1[i][j];
}
int main()
{
int n1 = 3;
int M1[3][2] = {
{2, 1},
{3, 1},
{2, 3}
};
init(n1, M1);
int i, j;
printf("作业在机器上的运行时间为:\n");
printf("\t\t机器1\t\t机器2\n");
for(i=0; i<n; i++)
{
printf("作业%d\t\t", i+1);
for(j=0; j<2; j++)
printf("%d\t\t", M[i][j]);
printf("\n");
}
int best = flowShop();
printf("最少完成时间和:%d\n", best);
printf("最优调度:\n");
for(i=0; i<n; i++)
printf("%d ", bestx[i]+1);
printf("\n");
return 0;
}