總共有n個裝配站;
底盤進入到裝配線1和裝配線2的時間記錄在二元陣列e[2]上;
底盤在裝配線1和裝配線2上每個站的時間記錄在陣列a1[n] 和a2[n]上;
底盤在每個站上換裝配線的時間記錄在t1[n-1], t2[n-2]上;(在最後乙個站時候, 不需要換裝配線了,所以陣列只有n-1個資料)
底盤離開裝配線的時間存放在二元陣列x[2]上面;
求得的最優解記錄在陣列f1[n] 和f2[n]上;
每次做出的選擇記錄在陣列l1[n-1], l2[n-1]上;(在最後乙個站的時候, 不需要做選擇了, 所以只有n-1個元素)
暴力法求解:
如果有n個裝配站, 每個裝配站都要考慮是選擇裝配線1還是裝配線2, 這樣時間複雜度就是2^n次方;
動態規劃演算法,子問題空間是一維的,每乙個子問題要兩次選擇,所以時間複雜度 = n *2;
動態規劃求解:
1) 步驟1(最優化結構分析)
將對裝配線1 和 裝配線2的求解區分開來;
對裝配線1而言,邊界是第乙個裝配站的解, f1[0] = e[0] + a1[0];
對第i個裝配站的解a(i), 可以由其子問題的解構成, 即只有乙個子問題a(i-1),即第i-1個解;(第i個問題的解是由第i-1個問題的解組成的。因此其子問題空間就是一維的;
對於每乙個子問題, 都有兩種選擇:選擇1 是選擇裝配站s(1, i-1); 選擇2 是選擇裝配站s(2, i-1);
因此,其時間複雜度 = 子問題空間 × 選擇次數 = n × 2 = 2n,
使用剪貼技術證明子問題的最優解構造了原問題的最優解:假設通過裝配站s(1, i)的最快路線通過了裝配站s(1, i-1), 那麼這個最快路線也是通過裝配站s(1, i-1)的最快路線;如果假設存在其他一條最快路線通過裝配站s(1, i-1), 那麼會導致通過裝配站s(1, i)的最快路線不是最快的,形參矛盾;
經過以上分析,可以得到乙個最優子結構,乙個問題的最優解包含了子問題的最優解,這是引用動態規劃的標誌之一。 因此可以使用動態規劃演算法。
以上相同的分析適用於裝配線2;
2) 步驟2 (乙個遞迴的解)
依據最優子結構, 乙個問題的最優解包含有子問題的最優解, 因此可以利用子問題的最優解來遞迴得到原問題的最優解;
最終的目標是求得底盤通過裝配線的最短時間, 即f*;
f* = min(f1[n] + x[0], f2[n] +x[1]);
因此需要求得f1[n] 和f2[n];
對於邊界: f1[0] = e[0] + a1[0]; f2[0] = e[1] + a2[0];
對於第i個裝配站: f1[i] = min(f1[i-1] + a1[i], f2[i-1] + t2[i-1] + a1[i]) , 使用min函式做出選擇
3) 步驟3(計算最快時間)
如果根據上面的遞迴式子使用自頂向下遞迴演算法編碼,其時間複雜度是z^n;
考慮計算f(i, j)的次數, 計算f(1, n)一次, 要計算f(1, n-1)2次,計算f(1, n-2) 4次,計算f(1, 1) 2^(n-1)次;
自頂向下的遞迴演算法實現如下:
void
fastest_way_recur_help
(int
* e,
int* x,
int* a1,
int* a2,
int* t1,
int* t2,
int*value1,
int*value2,
int n)
else
}int
fastest_way_recur
(int
* e,
int* x,
int* a1,
int* a2,
int* t1,
int* t2,
int n)
注意到f(i, j)的每乙個值僅僅依賴於f(1, i-1) 和f(2, i-1), 可以使用自底向上的動態規劃演算法:
自底向上的動態規劃**如下:
int
fastest_way
(int
* e,
int* x,
int* a1,
int* a2,
int* t1,
int* t2,
int* f1,
int* f2,
int* l1,
int* l2,
int n,
int* value)
else
//對於裝配線2
if(f2[i -1]
<
(f1[i -1]
+ t1[i -1]
))else
}//加上離開裝配線的時間後進行比較if(
(f1[n -1]
+ x[0]
)<
(f2[n -1]
+ x[1]
))else
}
4) 步驟4(構造通過工廠最快的線)
遞迴實現列印路徑**如下:
void
print_station_recur
(int
* l1,
int* l2,
int way,
int n)
非遞迴實現列印路徑**如下:
void
print_station
(int
* l1,
int* l2,
int way,
int n)
}
int
main()
;int x[2]
=;int t1[5]
=;int t2[5]
=;int a1[6]
=;int a2[6]
=;int value;
int* l1 =
(int*)
malloc
((n -1)
*sizeof
(int))
;int
* l2 =
(int*)
malloc
((n -1)
*sizeof
(int))
;int
* f1 =
(int*)
malloc
(n *
sizeof
(int))
;int
* f2 =
(int*)
malloc
(n *
sizeof
(int))
;int way =
fastest_way
(e, x, a1, a2, t1, t2, f1, f2, l1, l2, n,
&value)
;print_station
(l1, l2, way, n)
;printf
("value:%d\n"
, value)
;int result =
fastest_way_recur
(e, x, a1, a2, t1, t2, n)
;printf
("result: %d\n"
, result)
;return0;
}
15.1.1 修改程式print_station, 以站號遞增順序列印。
void
print_station_incre_recur
(int
* l1,
int* l2,
int way,
int n)
《演算法導論》筆記 第15章 15 1 裝配線排程
其實dp這一章可以跳過去的。不過經典問題還是有必要研究一下。15.1 1 說明應如何修改程式print stations,讓它以站號的遞增順序輸出各裝配站。若達到起點則輸出起點,否則遞迴向前。遞迴結束時輸出自身。15.1 2 利用替換法證明 在遞迴演算法中引用fi j 的次數ri j 等於2 n j...
演算法導論 動態規劃 裝配線排程
動態規劃 dynamic programming 是通過組合子問題的解而解決整個問題的。分治演算法是指將問題劃分為一些獨立的子問題,遞迴地求解各子問題,然後合併子問題的解而得到原問題的解。動態規劃適用於子問題不是獨立的情況,也就是各子問題包含公共的子子問題。在這種情況下,若用分治法則會做許多不必要的...
裝配線排程
問題描述 汽車生產工廠共有兩條裝配線,每條有n個裝配站 裝配線i 的第j 個裝配站表示為si,j,在該站的裝配時間為ai,j。乙個汽車底盤進入工廠,然後進入裝配線i i 為1或2 花費時間為ei。在通過一條線的第j 個裝配站後,這個底盤來到任一條線的第 j 1 個裝配站。如果它留在相同的裝配線,則沒...