1. 程式人生 > >【專題】拓撲排序 入門版

【專題】拓撲排序 入門版

(注:如果你是提高組+的大牛,敬可忽略本文 謝謝合作)

拓撲排序(標題一定要大大大~~~)

耙耙說,要先把概念搬出來:對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u線上性序列中出現在v之前。通常,這樣的線性序列稱為滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之為拓撲排序。

(這麼不要臉的從百度百科搬來真的好嗎。。。)、

好吧,其實你只要知道,什麼是有向無環圖,就夠了。。。(逗我呢)

先搬出來一道例題:

2歷史事件

【問題描述】

一些歷史迷們打算把歷史上的一些大事件按時間順序列出來。但是,由於資料不全,每個事件發生的具體時間都沒有找到。幸運的是,他們記得一些事件之間的先後關係。他們把事件分別編號123,……n,然後把一些先後關係列出。不過,這些複雜的先後關係仍然把他們難倒了。你能夠幫助他們嗎?

【輸入檔案】

輸入檔案event.in。第一行是兩個整數nm1<=n<=10001<=m<=100000),分別表示事件數和已知的先後關係數。接下來m行,第i行是兩個整數xiyi1<=xiyi<=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>
還有,資料好坑,輸入還有重複的情況。。。。。(逗我嗎)