標籤:
// 此博文為遷移而來,寫於2015年2月24日,不代表本人現在的觀點與看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vspe.html
1、前言在瞭解拓撲排序之前,我們先來看一道題目以更好的理解: 一個較大的工程往往被劃分成許多子工程,我們把這些子工程稱作活動(activity)。在整個工程中,有些子工程(活動)必須在其它有關子工程完成之後才能開始,也就是說,一個子工程的開始是以它的所有前序子工程的結束為先決條件的,但有些子工程沒有先決條件,可以安排在任何時間開始。為了形象地反映出整個工程中各個子工程(活動)之間的先後關係,可用一個有向圖來表示,圖中的頂點代表活動(子工程),圖中的有向邊代表活動的先後關係,即有向邊的起點的活動是終點活動的前序活動,只有當起點活動完成之後,其終點活動才能進行。通常,我們把這種頂點表示活動、邊表示活動間先後關係的有向圖稱做頂點活動網(Activity On Vertex network),簡稱AOV網。 課程代號 課程名稱 先修課程 C1 高等數學 無 C2 程式設計基礎 無 C3 離散數學 C1,C2 C4 資料結構 C3,C5 C5 演算法語言 C2 C6 編譯技術 C4,C5 C7 作業系統 C4,C9 C8 普通物理 C1 C9 電腦原理 C8 其AOV網:
2、什麼是拓撲排序? 一個AOV網應該是一個有向非循環圖,即不應該帶有迴路,因為若帶有迴路,則迴路上的所有活動都無法進行。一個具有三個頂點的迴路,由邊可得B活動必須在A活動之後,由邊可得C活動必須在B活動之後,所以推出C活動必然在A活動之後,但由邊可得C活動必須在A活動之前,從而出現矛盾,使每一項活動都無法進行。這種情況若在程式中出現,則稱為死結或死迴圈,是應該必須避免的。 在AOV網中,若不存在迴路,則所有活動可排列成一個線性序列,使得每個活動的所有前驅活動都排在該活動的前面,我們把此序列叫做拓撲序列(Topological order),由AOV網構造拓撲序列的過程叫做拓撲排序(Topological sort)。AOV網的拓撲序列不是唯一的,滿足上述定義的任一線性序列都稱作它的拓撲序列。例如,下面的三個序列都是的拓撲序列,當然還可以寫出許多。 (1) C1,C8,C9,C2,C3,C5,C4,C7,C6 (2) C2,C1,C3,C5,C4,C6,C8,C9,C7 (3) C1,C2,C3,C8,C9,C5,C4,C6,C7 3、有什麼作用? 除了上面我們之前我們展示的一道題目,拓撲排序的應用是比較廣泛的。下面我們再看一個例子: 士兵排隊 Soldier.cpp<1>問題描述 有N名士兵(1<=N<=26),編號依次為A,B,C,……進行隊列訓練時,指揮官要把一些士兵從高到矮依次排成一行,但現在指揮官不能直接獲得每個士兵的身高資訊,只能獲得“P1比P2高”這樣的比較結果(P1,P2∈{A,B,…Z},記為P1>P2),如“A>B”表示A比B高。編一程式,根據所得到的比較結果求出合格排隊方案。註:比較結果中沒有涉及到計程車兵不參加排隊。 例如,設有3個士兵,A、B、C,給出關係(A,B),(B,C)。其中(A,B)表示士兵A高於B,當上面的關係給出之後,可以將他們排成一隊:ABC。 <2>輸入 通過檔案輸入,每個比較結果在檔案中佔一行。<3>輸出 若輸入資料無解或不能唯一確定,則輸出“NO ANSWER!”,否則從高到矮依次輸出每一個士兵的編號,中間無分隔字元,並把結果寫入文字檔中。 <4>輸入範例A>BB>FF>D<5>輸出範例ABFD 代碼如下: #include<cstdio>#include<cstdlib>#define MAXN 28 struct edge{ int next,v;};edge list[MAXN*2];int head[MAXN],tot,n,have[MAXN],ListNum[MAXN],del[MAXN],get,ans[MAXN]; void AddList(char strU,char strV){ int u=strU-64,v=strV-64; have[u]=have[v]=1; tot++; list[tot].v=v; list[tot].next=head[u]; head[u]=tot; ListNum[v]++;} void init(){ char s[4]; freopen("3.in","r",stdin); freopen("3.out","w",stdout); while (scanf("%s",s)!=EOF) AddList(s[0],s[2]); for (int i=1;i<=26;i++) if (have[i]==1) n++;} int check(){ for (int i=1;i<=26;i++) if (del[i]!=1 && have[i]==1) return 0; return 1;} void DFS(int now,int dep){ del[now]=1; for (int x=head[now];x!=0;x=list[x].next) ListNum[list[x].v]--; ans[dep]=now; if (check()==1) { if (get==1) { printf("NO ANSWER!"); exit(0); } else { get=1; return; } } for (int i=1;i<=26;i++) if (ListNum[i]==0 && del[i]!=1 && have[i]==1) DFS(i,dep+1); for (int x=head[now];x!=0;x=list[x].next) ListNum[list[x].v]++; del[now]=0;} int main(){ init(); for (int i=1;i<=26;i++) if (ListNum[i]==0 && have[i]==1) DFS(i,1); if (get==0) printf("NO ANSWER!"); else for (int i=1;i<=n;i++) printf("%c",ans[i]+64); return 0;}
[知識點]拓撲排序