一個複雜的工程通常可以分解成一組小任務的集合,完成這些小任務意味著整個工程的完成。例如,汽車裝配工程可分解為以下任務:將底盤放上裝配線,裝軸,將座位裝在底盤上,上漆,裝刹車,裝門等等。任務之間具有先後關係,例如在裝軸之前必須先將底板放上裝配線。任務的先後順序可用有向圖表示——稱為頂點活動( Activity On Vertex, AOV)網路。有向圖的頂點代表任務,有向邊(i, j) 表示先後關係:任務j 開始前任務i 必須完成。圖1 - 4顯示了六個任務的工程,邊( 1 , 4)表示任務1在任務4開始前完成,同樣邊( 4 , 6)表示任務4在任務6開始前完成,邊(1 , 4)與(4 , 6)合起來可知任務1在任務6開始前完成,即前後關係是傳遞的。由此可知,邊(1 , 4)是多餘的,因為邊(1 , 3)和(3 , 4)已暗示了這種關係。
在很多條件下,任務的執行是連續進行的,例如汽車裝配問題或平時購買的標有“需要裝配”的消費品(單車、小孩的鞦韆裝置,割草機等等)。我們可根據所建議的順序來裝配。在由任務建立的有向圖中,邊( i, j)表示在裝配序列中任務i 在任務j 的前面,具有這種性質的序列稱為拓撲序列(topological orders或topological sequences)。根據任務的有向圖建立拓撲序列的過程稱為拓撲排序(topological sorting)。圖1 - 4的任務有向圖有多種拓撲序列,其中的三種為1 2 3 4 5 6,1 3 2 4 5 6和2 1 5 3 4 6,序列1 4 2 3 5 6就不是拓撲序列,因為在這個序列中任務4在3的前面,而任務有向圖中的邊為( 3 , 4),這種序列與邊( 3 , 4)及其他邊所指示的序列相矛盾。可用貪婪演算法來建立拓撲序列。演算法按從左至右的步驟構造拓撲序列,每一步在排好的序列中加入一個頂點。利用如下貪婪準則來選擇頂點:從剩下的頂點中,選擇頂點w,使得w 不存在這樣的入邊( v,w),其中頂點v 不在已排好的序列結構中出現。注意到如果加入的頂點w違背了這個準則(即有向圖中存在邊( v,w)且v 不在已構造的序列中),則無法完成拓撲排序,因為頂點v 必須跟隨在頂點w 之後。貪婪演算法的虛擬碼如圖1 3 - 5所示。while 迴圈的每次迭代代表貪婪演算法的一個步驟。
現在用貪婪演算法來求解圖1 - 4的有向圖。首先從一個空序列V開始,第一步選擇V的第一個頂點。此時,在有向圖中有兩個候選頂點1和2,若選擇頂點2,則序列V=2,第一步完成。第二步選擇V的第二個頂點,根據貪婪準則可知候選頂點為1和5,若選擇5,則V=2 5。下一步,頂點1是唯一的候選,因此V=2 5 1。第四步,頂點3是唯一的候選,因此把頂點3加入V
得到V=2 5 1 3。在最後兩步分別加入頂點4和6 ,得V=2 5 1 3 4 6。
1. 貪婪演算法的正確性
為保證貪婪演算法算的正確性,需要證明: 1) 當演算法失敗時,有向圖沒有拓撲序列; 2) 若
演算法沒有失敗,V即是拓撲序列。2) 即是用貪婪準則來選取下一個頂點的直接結果, 1) 的證明見定理1 3 - 2,它證明了若演算法失敗,則有向圖中有環路。若有向圖中包含環qj qj + 1.qk qj , 則它沒有拓撲序列,因為該序列暗示了qj 一定要在qj 開始前完成。
定理1-2 如果圖1 3 - 5演算法失敗,則有向圖含有環路。
證明注意到當失敗時| V |