lienhua34
2014-10-06
1 問題描述
某個汽車工廠共有兩條裝配線,每條有 n 個裝配站。裝配線 i 的第 j個裝配站表示為 Si,j ,在該站的裝配時間為 ai,j 。一個汽車底盤進入工廠,然后進入裝配線 i(i 為 1 或 2),花費時間為 ei 。在通過一條線的第 j 個裝配站后,這個底盤來到任一條裝配線的第(j+1)個裝配站。如果它留在相同的裝配線,則沒有移動開銷。但是,如果它移動到另一條線上,則花費時間為 ti,j 。在離開一條裝配線的第 n 個裝配站后,完成的汽車底盤花費時間為 xi 離開工廠。待求解的問題是,確定應該在裝配線 1 內選擇哪些站,在裝配線 2 內選擇哪些站,才能使汽車通過工廠的總時間最短。
2 算法解析
下面通過動態規划思想來對該裝配線調度問題分步驟進行解析。
2.1 步驟 1:描述最優解結構的特征
觀察一條通過裝配站 S1,j 的最快路線,發現它必定是經過裝配線 1 或2 上的裝配站(j-1)。因此,通過裝配站 S1,j 的最快路線只能是以下二者之一:
• 通過裝配站 S1,j−1 的最快路線,然后直接通過裝配站 S1,j 。
• 通過裝配站 S2,j−1 的最快路線,從裝配線 2 轉移到裝配線 1,然后通過裝配站 S1,j 。
故尋找通過任一條裝配線上的裝配站 j 的最快路線,我們可以規划為先尋找通過兩條裝配線上的裝配站 j-1 的最快路線。
2.2 步驟 2: 利用問題的最解遞歸定義一個最優解的值
記 fi [j] 表示一個汽車底盤從起點到裝配站 Si,j 的最快可能時間;f ∗ 表示底盤通過工廠的所有裝配站的最快時間。
2.3 步驟 3:計算最快時間
遞歸算法的執行時間是關於 n 的指數形式,所以我們采用從低向上的實現方法計算最快時間。記 li [j] 表示進入裝配站 Si,j 的前一個裝配站來自哪條裝配線;l∗ 表示從哪一條裝配線離開工廠。
FATEST-WAY(a, t, e, x, n) f1 [1] ← e1 + a1,1 f2 [1] ← e2 + a2,1 for j ← 2 to n if f1 [j − 1] + a1,j ≤ f2 [j − 1] + t2,j−1 + a1,j then f1 [j] ← f1 [j − 1] + a1,j l1 [j] ← 1 else f1 [j] ← f2 [j − 1] + t2,j−1 + a1,j l1 [j] ← 2 endif if f2 [j − 1] + a2,j ≤ f1 [j − 1] + t1,j−1 + a2,j then f2 [j] ← f2 [j − 1] + a2,j l2 [j] ← 2 else f2 [j] ← f1 [j − 1] + t1,j−1 + a2,j l2 [j] ← 1 endif endfor if f1 [n] + x1 ≤ f2 [n] + x2 then f∗ ← f1 [n] + x1 l∗ ← 1 else f∗ ← f2 [n] + x2 l∗ ← 2 endif end
2.4 步驟 4:構造通過工廠的最快路線
根據步驟 3 獲得的 li [j] 和 l∗ 來打印出裝配線調度耗時最小的路線。
PRINT-STATIONS(l, l∗ , n) i ← l∗ print ’line:’ i ’, station:’ n for j ← n downto 2 i ← li [j] print ’line:’ i ’, station:’ j-1 endfor end
3 C 語言實現驗證
現在假設有兩條各自有 5 個裝配站的裝配線,其裝配線各站點調度耗時如下圖所示,
圖 1: 裝配線示例圖
其中,裝配線左邊是入口,右邊是出口,入口和出口上的數字分別表示進入裝配線和離開裝配線的耗時。綠色圓圈表示裝配站,圓圈中的數字表示在該裝配站上的裝配時間。各個裝配線之間連線上的數字表示從一個條裝配線的轉移到另一條裝配線消耗的時間。
使用 C 語言的驗證代碼如下所示,

#include <stdlib.h> #include <stdio.h> void PrintPath(int *, int , int); void FastestWay(int *, int *, int [], int [], int); int main(void) { int enterTimes[2] = {1, 2}; int exitTimes[2] = {2, 1}; int assemblyTimes[2][5] = { {4, 2, 3, 5, 3}, {7, 2, 4, 2, 4}}; int transferTimes[2][5] = { {2, 1, 2, 2, 1}, {1, 1, 2, 4, 2}}; FastestWay(&assemblyTimes[0][0], &transferTimes[0][0], enterTimes, exitTimes, 5); exit(0); } void FastestWay(int *assemblyTimes, int *transferTimes, int enterTimes[2], int exitTimes[2], int n) { int fastestTime = 0; int exitLine; int j; int *fastest, *lineTag; int temp1, temp2; fastest = malloc(sizeof(int) * 2 * n); lineTag = malloc(sizeof(int) * 2 * n); *(fastest) = enterTimes[0] + *assemblyTimes; *(fastest + n) = enterTimes[1] + *(assemblyTimes + n); for (j = 1; j < n; j++) { temp1 = *(fastest + (j-1)); temp2 = *(fastest + (n + j - 1)) + *(transferTimes + (n + j - 1)); if (temp1 <= temp2) { *(fastest + j) = temp1 + *(assemblyTimes + j); *(lineTag + j) = 1; } else { *(fastest + j) = temp2 + *(assemblyTimes + j); *(lineTag + j) = 2; } temp1 = *(fastest + (n + j - 1)); temp2 = *(fastest + (j - 1)) + *(transferTimes + (j - 1)); if (temp1 <= temp2) { *(fastest + (n + j)) = temp1 + *(assemblyTimes + (n + j)); *(lineTag + (n + j)) = 2; } else { *(fastest + (n + j)) = temp2 + *(assemblyTimes + (n + j)); *(lineTag + (n + j)) = 1; } } temp1 = *(fastest + (n - 1)) + exitTimes[0]; temp2 = *(fastest + (n + n - 1)) + exitTimes[1]; if (temp1 <= temp2) { fastestTime = temp1; exitLine = 1; } else { fastestTime = temp2; exitLine = 2; } printf("The cost of the fastest path: %d\n", fastestTime); PrintPath(lineTag, exitLine, n); } void PrintStation(int line, int station) { printf("line: %d, station: %d\n", line, station); } void PrintPath(int *lineTag, int exitLine, int n) { int i = exitLine; int j; PrintStation(exitLine, 5); for (j = 5; j >= 2; j--) { i = *(lineTag + (n * (i - 1) + j - 1)); PrintStation(i, j-1); } }
編譯該程序,生成並執行文件 AssemblyLineSchedule,
lienhua34:algorithm$ gcc -o AssemblyLineSchedule AssemblyLineSchedule.c lienhua34:algorithm$ ./AssemblyLineSchedule The cost of the fastest path: 19 line: 2, station: 5 line: 2, station: 4 line: 2, station: 3 line: 1, station: 2 line: 1, station: 1
於是,最短路徑總共耗時 19,其路徑如下圖中的紅色線條,
圖 2: 含有最快路徑的裝配線示例圖
(done)