圖論-拓撲排序詳解


拓撲排序(topsort)詳解

這篇隨筆就信息學奧林匹克競賽中圖論的一個知識點——拓撲排序進行講解。拓撲排序的內容比較基礎,只要求讀者學習過並了解信息學中圖的相關定義和一些專業名詞,但是拓撲排序的變形題目比較多,希望讀者在看完本隨筆后認真體會練習,掌握拓撲排序。

上課!

拓撲排序的定義

顧名思義,這是一種排序,確切地說,是一種圖上排序,在一張有向無環圖(注解:有向無環圖即很多參考書和題解中所說的DAG)上進行排序,把其中的所有節點排成一個序列,使得圖中的任意一對有邊相連的節點(u,v)u要出現在v前。

所以我再次強調,拓撲排序只能用在有向無環圖中!!

這樣的線性序列我們稱之為拓撲序。

注意,拓撲序不唯一!這個地方不明白的請自己畫圖理解(或者參考下面的那棵樹)。

拓撲排序的實現原理

在講解圖的拓撲排序之前,我們可以用一棵樹來加深對拓撲排序的理解(因為樹是絕對沒有環)。

我們隨意地定義一棵有向樹(如下圖),如果我們想得到它的拓撲序,那會很簡單,只需要先把根節點8號放進隊列中,然后再放8號的任意一個兒子節點,繼續此操作。直到節點全放進去為止。

我們會發現,問放進去的是任意的一個子節點,所以我們說拓撲序是不唯一的(在絕大多數情況下,你要非跟我抬杠說假如只有一條鏈,我也沒辦法)。

拓撲排序的實現

講完了實現原理,我們來進行拓撲排序的代碼實現,根據上面的原理,我們會發現,我們要保證拓撲序列的正確性,只需要把圖中的入度為0的節點先放進拓撲序,然后把這個點和它所有的出邊全部刪掉,這樣就還會出現一些入度為0的點,我們繼續重復以上操作。

有細心的小伙伴會發現這個和算法中的寬搜很相似,沒錯,所謂寬搜和深搜,都是基於對樹與圖的深度/寬度優先遍歷而定義的,所以拓撲排序的實現其實就是借助了寬搜的思想。

上模板:

void topsort()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(rudu[j]==0)
            {
                x=j;
                top[++cnt]=j;
                rudu[j]--;
                break;
            }
        }
        for(int j=head[x];j;j=nxt[j])
            rudu[j]--;
    }
}

以上代碼的top數組存拓撲序列,使用的是鏈式前向星存圖並遍歷。比較好理解,但是時間復雜度比較低。(所以僅供理解)

所以我們用C++STL來實現拓撲排序,這樣會快很多。

模板:

void topsort()
{
    queue<int> q;
    for(int i=1;i<=n;i++)
        if(rudu[i]==0)
            q.push(i);
    while(q.empty())
    {
        int x=q.front();
        q.pop();
        top[++cnt]=x;
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            rudu[y]--;
            if(rudu[y]==0)
                q.push(y);
        }
    }
}

其實也很好理解啦...

注意,以上代碼是針對於已經保證圖是DAG的情況下而出現的,假如我們沒有題目中DAG的保證,就要額外地判這個圖是不是DAG,即很多題目中要求的“無解”情況。

怎么判斷呢?

學到這里,我覺得你應該想到,如果最后得到的拓撲序列的長度等於節點總數,那么這個圖就是DAG,否則就不是。

所以我們最后進行判斷。

(你也可以使用STL中的vector容器)

代碼:

if(cnt==n)
    //DAG操作
else
    //非DAG操作

拓撲排序的用途及一些技巧

拓撲排序的用途是解決一些依賴關系的題,一般來講沒有圖論的基本要素(告訴你幾個點,一眼就看出來這是一道圖論題%%%),所以,我認為做拓撲排序題的難點在於如何建立一個和題意相符的圖(建圖坑死爹)。所以美其名曰拓撲排序是圖論中最簡單的內容,其實它的相關題目都很有思維含量,所以強烈建議各位同學多刷題多刷題

由於拓撲排序不唯一,所以有些坑爹題目要求拓撲序列的一些內容,比如按字典序等等。

這時我們把原本的隊列拓撲排序換成優先隊列拓撲排序。

注意,優先隊列不能提速,不要以為找到了一份更好的模板,一定要讀題~~!!

除了定義方式有點怪異其他的跟隊列一樣。

priority_queue<int,vector<int>,greater<int> >q;
//取隊首的時候需要變成q.top();

下課!!祝同學們AK IOI!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM