【專題】拓撲排序 入門版
(注:如果你是提高組+的大牛,敬可忽略本文 謝謝合作)
拓撲排序(標題一定要大大大~~~)
耙耙說,要先把概念搬出來:對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u線上性序列中出現在v之前。通常,這樣的線性序列稱為滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之為拓撲排序。
(這麼不要臉的從百度百科搬來真的好嗎。。。)、
好吧,其實你只要知道,什麼是有向無環圖,就夠了。。。(逗我呢)
先搬出來一道例題:
題2歷史事件
【問題描述】
一些歷史迷們打算把歷史上的一些大事件按時間順序列出來。但是,由於資料不全,每個事件發生的具體時間都沒有找到。幸運的是,他們記得一些事件之間的先後關係。他們把事件分別編號1,2,3,……n,然後把一些先後關係列出。不過,這些複雜的先後關係仍然把他們難倒了。你能夠幫助他們嗎?
【輸入檔案】
輸入檔案event.in。第一行是兩個整數n,m(1<=n<=1000,1<=m<=100000),分別表示事件數和已知的先後關係數。接下來m行,第i行是兩個整數xi,yi(1<=xi,yi<=n),表示事件xi比事件yi先發生。
【輸出檔案】
輸出檔案event.out。按事件發生的時間順序列出事件的編號,每行一個,若存在多種可能,輸出第一個事件編號最小的,若第一個事件編號相同,則輸出第二個事件編號最小的……;若沒有滿足條件的編號序列,輸出一行’Error!’)。
【樣例輸入】
3 2
1 2
1 3
【樣例輸出】
1
2
3
這題真的是純拓撲純拓撲純拓撲(重要的事情說三遍)
實際上,題目的意思就是拓撲的作用、原理吧。。。
以上圖為例(請勿吐槽)先說明一些東西:
入度:即有多少條邊指向某個頂點(有向圖) 例如上圖中頂點2的入度為2
出度:即某個頂點向外指向的邊有多少條 例如上圖中頂點2的出度為1
每一次,選一個入度為 0 的頂點輸出(如上圖頂點1),然後將其所有後繼頂點的入度-1(即把這個頂點往外伸展的邊刪除),重複這兩步直至輸出所有頂點,或找不到入度為 0 的頂點為止(這就是有“環”的情況)
上面的例子排序後的序列為0 1 2 4 3 5 7 8 (自己演算去)
找到一個入度為0的點並處理完後,再重新從1到n找(越小越好)
<span style="font-size:14px;color:#330033;">var n,m,i,x,y,t,j,k:longint;
rd,cd,b,c:array[1..1001] of longint;
f:array[1..1001,1..1001] of longint;
p:boolean;
{procedure tuopu;
var i,j:longint;
p:boolean;
begin
p:=false;
for i:=1 to n do begin
if rd[i]=0 then begin
p:=true;
rd[i]:=-1;
for j:=1 to n do begin
if (f[i,j]=1)and(i<>j) then begin
f[i,j]:=0;
dec(rd[j]);
end;
end;
inc(t);
c[t]:=i;
end;
end;
if not p then begin
writeln('Error!');
halt;
end;
end;} (請忽略此段,錯誤打法)
procedure ex;
begin
writeln('Error!');
close(input);
close(output);
halt;
end;
begin
assign(input,'event.in');reset(input);
assign(output,'event.out');rewrite(output);
readln(n,m);
for i:=1 to m do begin
readln(x,y);
if f[x,y]=1 then continue;
f[x,y]:=1;
inc(rd[y]);
end;
i:=0;
while i<n do begin
p:=false;
j:=1;
for k:=1 to n do if rd[k]=0 then begin (判斷是否有環)
p:=true;
break;
end;
if not p then ex;
while rd[j]<>0 do inc(j);(找入度為0的點)
rd[j]:=-1;
for k:=1 to n do if f[j,k]=1 then begin
dec(rd[k]);
f[j,k]:=0;
end;(處理)
inc(i);
c[i]:=j;
end;
for i:=1 to n do writeln(c[i]);
close(input);
close(output);
end.</span><span style="color:#003366;font-size:18px;">
</span>
還有,資料好坑,輸入還有重複的情況。。。。。(逗我嗎)