1、問題描述
每一個作業Ji都有兩項任務分別在2台機器上完成。每個作業必須先有機器1處理,然后再由機器2處理。作業Ji需要機器j的處理時間為tji。對於一個確定的作業調度,設Fji是作業i在機器j上完成處理時間。則所有作業在機器2上完成處理時間和f是指把F2i將i從1-n求和,稱為該作業調度的完成時間和。
2、簡單描述
對於給定的n個作業,指定最佳作業調度方案,使其完成時間和達到最小。
區別於流水線調度問題:批處理作業調度旨在求出使其完成時間和達到最小的最佳調度序列;
流水線調度問題旨在求出使其最后一個作業的完成時間最小的最佳調度序列;
例:設n=3,考慮以下實例:
這3個作業的6種可能的調度方案是1,2,3;1,3,2;2,1,3;2,3,1;3,1,2;3,2,1;它們所相應的完成時間和分別是19,18,20,21,19,19。易見,最佳調度方案是1,3,2,其完成時間和為18。那么具體的完成時間和是怎么計算的呢?將在第3部分舉例說明中詳細描述。
3、舉例說明
4、算法設計
批處理作業調度問題要從n個作業的所有排列中找出有最小完成時間和的作業調度,所以批處理作業調度問題的解空間是一顆排列樹。
按照回溯法搜索排列樹的算法框架,設開始時x=[1,2, ... , n]是所給的n個作業,則相應的排列樹由x[1:n]的所有排列(所有的調度序列)構成。
二維數組M是輸入作業的處理時間,bestf記錄當前最小完成時間和,bestx記錄相應的當前最佳作業調度。
在遞歸函數Backtrack中,
當i>n時,算法搜索至葉子結點,得到一個新的作業調度方案。此時算法適時更新當前最優值和相應的當前最佳調度。
當i<n時,當前擴展結點位於排列樹的第(i-1)層,此時算法選擇下一個要安排的作業,以深度優先方式遞歸的對相應的子樹進行搜索,對不滿足上界約束的結點,則剪去相應的子樹。
5、算法分析
【前期准備】
1、區分作業i和當前第i個正在執行的作業
給x賦初值,即其中一種排列,如x=[1,3,2];M[x[j]][i]代表當前作業調度x排列中的第j個作業在第i台機器上的處理時間;如M[x[2]][1]就意味着作業3在機器1上的處理時間。
2、bestf的初值
此問題是得到最佳作業調度方案以便使其完成時間和達到最小,所以當前最優值bestf應該初始化賦值為較大的一個值。
3、f1、f2的定義與計算
假定當前作業調度排列為:x=[1,2,3];f1[i]即第i個作業在機器1上的處理時間,f2[j]即第j個作業在機器2上的處理時間;則:
f1[1]=M[1][1] , f2[1]=f1[1]+M[1][2]
f1[2]=f1[1]+M[2][1] , f2[2]=MAX(f2[1],f1[2])+M[2][2] //f2[2]不光要等作業2自己在機器1上的處理時間,還要等作業1在機器2上的處理時間,選其大者。
f1[3]=f1[2]+M[3][1] , f2[3]=MAX(f2[2],f1[3])+M[3][2]
1只有當前值有用,可以覆蓋賦值,所以定義為int型變量即可,減少空間消耗;f2需要記錄每個作業的處理時間,所以定義為int *型,以便計算得完成時間和。
4、f2[0]的初值
f2[i]的計算都是基於上一個作業f2[i-1]進行的,所以要記得給f2[0]賦值為0。
6.代碼實現

#include<iostream> using namespace std; int x[100]; //當前作業調度————其中一種排列順序 int bestx[100]; //當前最優作業調度 int m[100][100];//各作業所需的處理時間 //M[j][i]代表第j個作業在第i台機器上的處理時間 int f1=0;//機器1完成處理時間 int f2=0;//機器2完成處理時間 int cf=0;//完成時間和 int bestf=10000;//當前最優值,即最優的處理時間和 int n;//作業數 void swap(int &a,int &b) { int temp=a; a=b; b=temp; } void Backtrack(int t) { //t用來指示到達的層數(第幾步,從0開始),同時也指示當前執行完第幾個任務/作業 int tempf,j; if(t>n) //到達葉子結點,搜索到最底部 { if(cf<bestf) { for(int i=1; i<=n; i++) bestx[i]=x[i];//更新最優調度序列 bestf=cf;//更新最優目標值 } } else //非葉子結點 { for(j=t; j<=n; j++) //j用來指示選擇了哪個任務/作業(也就是執行順序) { f1+=m[x[j]][1];//選擇第x[j]個任務在機器1上執行,作為當前的任務 tempf=f2;//保存上一個作業在機器2的完成時間 f2=(f1>f2?f1:f2)+m[x[j]][2];//保存當前作業在機器2的完成時間 cf+=f2; //在機器2上的完成時間和 //如果該作業處理完之后,總時間已經超過最優時間,就直接回溯。 //剪枝函數 if(cf<bestf) //總時間小於最優時間 { swap(x[t],x[j]); //交換兩個作業的位置,把選擇出的原來在x[j]位置上的任務調到當前執行的位置x[t] Backtrack(t+1); //深度搜索解空間樹,進入下一層 swap(x[t],x[j]); //進行回溯,還原,執行該層的下一個任務 //如果是葉子節點返回上一層 } //回溯需要還原各個值 f1-=m[x[j]][1]; cf-=f2; f2=tempf; } } } int main() { int i,j; cout<<"請輸入作業數:"<<endl; cin>>n; cout<<"請輸入在各機器上的處理時間"<<endl; for(i=1; i<=2; i++) //i從1開始 for(j=1; j<=n; j++) cin>>m[j][i];//第j個作業,第i台機器的時間值 for(i=1; i<=n; i++) x[i]=i;//初始化當前作業調度的一種排列順序 Backtrack(1); cout<<"調度作業順序:"<<endl; for(i=1; i<=n; i++) cout<<bestx[i]<<' '; cout<<endl; cout<<"處理時間:"<<endl; cout<<bestf; return 0; } /* 測試數據: 3 2 3 2 1 1 3 3 2 5 4 3 2 1 */
7.實現結果
參考文獻:王曉東《算法設計與分析》
https://blog.csdn.net/qian2213762498/article/details/79420060