之前講過一個相似的問題流水作業調度問題,那一道題最開始用動態規划,推到最后得到了一個Johnson法則,變成了一個排序問題,有興趣的可以看一下https://www.cnblogs.com/wkfvawl/p/11667092.html
本篇博客主要參考自https://blog.csdn.net/qq_40685275/article/details/80403976
一、問題描述
給定n個作業的集合{J1,J2,…,Jn}。每個作業必須先由機器1處理,然后由機器2處理。作業Ji需要機器j的處理時間為tji。對於一個確定的作業調度,設Fji是作業i在機器j上完成處理的時間。所有作業在機器2上完成處理的時間和稱為該作業調度的完成時間和。批處理作業調度問題要求對於給定的n個作業,制定最佳作業調度方案,使其完成時間和達到最小。
例:設n=3,考慮以下實例:
看到這里可能會對這些完成時間和是怎么計算出來的會有疑問,這里我拿123和312的方案來說明一下。
對於調度方案(1,2,3)
作業1在機器1上完成的時間是2,在機器2上完成的時間是3
作業2在機器1上完成的時間是5,在機器2上完成的時間是6
作業3在機器1上完成的時間是7,在機器2上完成的時間是10
所以,作業調度的完成時間和= 3 + 6 + 10
這里我們可以思考一下作業i在機器2上完成的時間應該怎么去求?
作業i在機器1上完成的時間是連續的,所以是直接累加就可以。但對於機器2就會產生兩種情況,這兩種情況其實就是上圖的兩種情況,對於(1,2,3)的調度方案,在求作業2在機器2上完成的時間時,由於作業2在機器1上還沒有完成,這就需要先等待機器1處理完;而對於(3,1,2)的調度方案,在求作業2在機器2上完成的時間時,作業2在機器1早已完成,無需等待,直接在作業1被機器1處理之后就能接着被處理。
綜上,我們可以得到如下表達式
if (F2[i-1] > F1[i]) F2[i] = F2[i-1] + t[2][i] else F2[i] = F1[i] + t[2][i]
二、算法設計


類Flowshop的數據成員記錄解空間的結點信息,M輸入作業時間,bestf記錄當前最小完成時間和,數組bestx記錄相應的當前最佳作業調度。
數組x[i],bestx[i],二維數組m[j][i];
數組x記錄當前調度;
bestx記錄當前最優調度;
初始時,x[i]=i ; bestx[i]=∞; (i=0,1,......,n)
二維數組m記錄各作業分別在兩台機器上的處理時間;
m[j][i]表示在第i台機器上作業j的處理時間
變量f1,f2,cf,bestf;
f1記錄作業在第一台機器上的完成時間;
f2記錄作業在第一台機器上的完成時間;
cf記錄當前在第二台機器上的完成時間和;
bestf記錄當前最優調度的完成時間和;
在遞歸函數Backtrack中,
當i>n時,算法搜索至葉子結點,得到一個新的作業調度方案。此時算法適時更新當前最優值和相應的當前最佳調度。
當i<=n時,當前擴展結點在i層,以深度優先方式,遞歸的對相應子樹進行搜索,對不滿足上界約束的結點,則剪去相應的子樹。
這里注意一下該程序的輸入,要現將機器1對應所有作業的處理時間輸入,再輸入機器2的,對應上面的例子的數據就是 232113
#include <stdio.h> int x[100],bestx[100],m[100][100];//m[j][i]表示在第i台機器上作業j的處理時間 //數組bestx記錄相應的當前最佳作業調度。 int f1=0,f2,cf=0,bestf=10000,n; //bestf記錄當前最小完成時間和 void swap(int *x,int t,int j) { int temp = x[t]; x[t] = x[j]; x[j] = temp; } void Backtrack(int t) { int tempf,j,i; if(t>n) //到達葉子結點,搜索到最底部 { for( i=1; i<=n; i++) { bestx[i]=x[i]; } bestf=cf; } else //非葉子結點 { for(j=t; j<=n; j++) { f1+=m[x[j]][1]; //記錄作業在第一台機器上的完成處理時間 tempf=f2;//保存上一個作業在機器2的完成處理時間 f2=(f1>f2?f1:f2)+m[x[j]][2];//保存當前作業在機器2的完成時間 cf+=f2; //cf記錄當前在機器2上的完成時間和 if(cf<bestf) { swap(x,t,j); //交換兩個作業的位置 Backtrack(t+1); swap(x,t,j); } f1-=m[x[j]][1]; cf-=f2; f2=tempf; } } } int main() { int i,j; printf("請輸入作業數量\n"); scanf("%d",&n); printf("請輸入在各機器上的處理時間\n"); for(i=1; i<=2; i++) { for(j=1; j<=n; j++) { scanf("%d",&m[j][i]); } } for(i=1; i<=n; i++) { x[i]=i; //記錄當前調度 } Backtrack(1); printf("調度作業順序\n"); for(i=1; i<=n; i++) { printf("%d\t",bestx[i]); } printf("\n"); printf("處理時間:\n"); printf("%d\n",bestf); return 0; }
注意swap函數,交換兩個作業的位置相當於重新賦值了,所以該程序沒有對x[i]的賦值函數
三、算法的效率

