演算法設計與分析:第五章 回溯法 5.8流水作業車間排程
阿新 • • 發佈:2019-01-25
/* 流水作業車間排程: n個作業要在兩臺機器M1和M2組成的流水線上完成加工。每個作業加工的順序都是現在M1上加工,然後在 M2上加工。M1和M2加工作業i所需的時間分別為ai和bi。流水作業排程問題要求確定這n個作業的最優 加工順序,使得從第一個作業在機器M1上開始加工,到最後一個作業在機器M2上加工完成所需的時間 最少。作業在機器M1和M2的加工順序相同。 分析: 解空間:是排列樹,用陣列x[](初值為1,2,3,...,n)模擬不同排列 如何計算完成時間? 對機器M1進行加工,加工時間f1是固定的。f1[i] = f1[i-1] + M1[x[i]] 對機器M2加工分為兩種情況 1)有空閒 ##a1........##a2...........## ##b1.... ##b2.........## 空閒時,f2[i] = f1[i] + M2[x[i]] 2)積壓時 ##a1........##a2...........## ##b1..................##b2.........## 空閒時,f2[i] = f2[i-1] + M2[x[i]] 如何獲取最優解? 最優排程應使及其M1沒有空閒時間,機器M2空閒時間最少。 在搜尋排列樹的同時,不斷更新最優解,最後找到問題的最優解。 搜尋過程中,當某一排列的前幾步的加工時間已經大於當前的最小值,就無需搜尋計算。 資料結構設計: 二維陣列job[100][2]儲存作業在M1,M2的加工時間。 由於f1在計算中,只需當前值,用變數儲存即可;f2在計算時,還依賴前一個作業的資料, 所以用陣列儲存。 變數f儲存當前加工需要的全部時間。 輸入: 3(作業數) 2 1 3 1 2 3 輸出: 8 演算法步驟: 1 如果搜尋到葉節點(必然是最優的),得到排程方案,更新 2 對當前作業直到最後作業做迭代 3 計算機器1工作時間 , 判斷機器2處於積壓或者空閒 並更新f2 4 更新直到當前結點的累加值 5 若累加值小於最優解,則進入該結點的葉子結點,交換兩個作業,回溯下一個結點,再交換回來 6 將f1設定為回溯前的值,將累加值還原為回溯前的值 7 這是排列樹問題,初始化應該給與一個排列 //這是回溯中的排列樹問題:需要賦予一個初始排列。排列樹:選取的n個元素滿足某種排列性質,時間複雜度為O(n!) for(int k = 1 ; k <= n ; k++) { //設定一個初始排程為排程作業1,2,...,n的順序 x[k] = k; } 8 初始最優值應該置為無窮大 bestf = 1000000000;//易錯,最優值應該初始化為無窮大 */ #include <iostream> using namespace std; int n;//作業數 int bestf;//存放最優排程時間 int f1;//機器1的呼叫時間 int f2[100];//機器2的呼叫時間 int f;//直至當前結點的機器呼叫累加時間 int job[100][3];//作業的執行時間 int x[100];//易錯,當前排程 int bestx[100];//易錯,存放當前作業最佳排程 void swap(int* p1 , int* p2) { int iTemp = *p1 ; *p1 = *p2; *p2 = iTemp; } void backTrace(int i) { if(i == n + 1)//如果到達葉子結點,則說明產生了排程方案,更新 { for(int j = 1 ; j <= n ; j++) { bestx[j] = x[j]; } bestf = f; } else { //回溯的標準格式 for(int j = i ; j <= n ; j++) { f1 = f1 + job[ x[j] ][1]; //如果機器2存在積壓 if(f2[i-1] > f1) { f2[i] = f2[i-1] + job[ x[j] ][2]; } else { f2[i] = f1 + job[ x[j] ][2]; } f = f + f2[i]; //判斷累加和是否小於最優排程值,是就進行繼續嘗試下一個結點 if(f < bestf) { swap(&x[i] , &x[j]); backTrace(i+1); swap(&x[i] , &x[j]); } //清理現場 f1 = f1 - job[ x[j] ][1]; f = f - f2[i]; } } } void process() { while(cin >> n) { //輸入 memset(job , 0 , sizeof(job)); for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j <= 2 ; j++) { cin >> job[i][j]; } } //計算 f = 0; f1 = 0; bestf = 1000000000;//易錯,最優值應該初始化為無窮大 memset(f2 , 0 , sizeof(f2)); //易錯,這是回溯中的排列樹問題:需要賦予一個初始排列。排列樹:選取的n個元素滿足某種排列性質,時間複雜度為O(n!) for(int k = 1 ; k <= n ; k++) { //設定一個初始排程為排程作業1,2,...,n的順序 x[k] = k; } backTrace(1); cout << f2[n] << endl; } } int main(int argc , char* argv[]) { process(); getchar(); return 0; }