有向圖歐拉回路的快速算法(POJ 2230題解)

高考考完開始進行算法競賽的康復訓練(好吧實際上是從零開始直接學,過完高三什麼都忘了T T),在POJ上作了一些水題。今天作到這道歐拉回路,頗有感觸,由於第一次學這個算法的時候並無學透,今天再作才發現原來求歐拉回路的算法有這麼精妙。git

先講題意吧,原文連接:http://poj.org/problem?id=2230。大意是給你一個無向圖,要求找一條路徑,走過每一條邊剛好兩次,且每次走的方向不一樣。很容易就能想到把這圖轉化爲有向圖求歐拉回路。題目保證必定能找到從1點出發回到1點的答案。算法

如今的關鍵就是求這個迴路了,一個樸素的想法就是直接DFS,每次通過一條邊就刪除它,回退的時候再還原。若是通過了原圖邊數剛好兩倍就找到了一個答案。但這個算法實在太慢,直接TLE。segmentfault

如今咱們考慮搜索的過程:若是不還原一個點所遍歷的邊,那麼當這個點去掉全部邊以後,再去掉這個點,剩下的圖是什麼?先上圖,這是題目所給的樣例的草圖:
還沒遍歷時的圖像
而後從1開始,咱們喪心病狂一點,直接把1就搞掉不碰與1無關的邊,反正遍歷的順序是不肯定的,這麼作何嘗不可。那麼就變成了這樣:
刪除1和相關的邊的狀況
咱們看到,剩下的仍是一個歐拉回路。咱們能夠猜測,一個歐拉回路,刪掉一個點,仍然是一個歐拉回路。這個結論其實很是容易證實,隨便想一想就明白這是對的。進一步研究,會發現一個更廣泛的結論:從一個歐拉回路中拖走一個小歐拉回路,結果也是一個歐拉回路。這個性質也很明顯,並且前一個結論是後一個結論的推論。由這個結論,咱們能夠考慮,有公共點的兩個歐拉回路實際上是能夠拼接的。因而下面這個搜索算法就很好理解了:spa

void DFS(int now)
{
    for (int p=G.Head[now];p;p=G.Pre[p])
    {
        if (!G.Vis[p])
        {
            G.Vis[p]=1;
            DFS(G.V[p]);
        }
    }
    printf("%d\n",now);
}

G是圖,我用了一個鄰接表,G.Vis是標記這條邊是否走過。若是一條邊還沒走過,就標記而後走下去,關鍵在若是一個點已經走完了怎麼辦:直接輸出。每次從v出發回到v,就是扒走了一個迴路,根據棧的性質,這樣獲得的順序實際上是相反的。不過因爲這題的圖中邊是成對出現,因此不要緊,倒過來也是能夠的。輸出的過程就是把一個個歐拉回路拼在一塊兒。對於無向圖,經過拼接也能夠獲得歐拉回路,不過相對於這個算法要複雜很多。最後提供Gitcafe代碼連接,若有須要參考請看:https://gitcafe.com/linmx0130/OJCode/blob/master/POJ/P2230/main.cppcode

相關文章
相關標籤/搜索