Tarjan 小結

Quetion : 判斷in_stack[]有什麼意義?


<0>
    if(dfn[i] != 0) low[u] = min(low[u], dfn[v]);
    當只是強連通搞縮點時,可以換爲min(low[u], low[v]),因爲縮點只要把相鄰的一塊搞跟min(low[xxx])一塊兒就行了;
    但是搞雙連通就不可以了,因爲雙連通對割點/橋的判定需要用到low[v],後面略..


    
    無向圖的Tarjan判斷邊是否出現過僅僅爲了防止逆向邊的使用...我還以爲有什麼神奇的作用呢...-_-|| 如果是有向圖的話就免了
    當重邊只算一條的時侯,既可以用上訴的判邊法,也可用判點法,即記錄每個節點在dfs樹的父節點,然後每次都判斷一下fa[u] == v ?



<1> 求強連通塊    //應用: 縮點


void Tarjan(int u)
{
    dfn[u] = low[u] = Index++;
    S[++top] = i;
    for(int k = head[u]; k != -1; k = edge[k].next) {
                    //TODO      什麼時候要判斷點v或者邊k是否出現過?
        int v = edge[k].v;
        if(!dfn[v]) {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if(in_stack[v]) low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u]) {
        Order++;
        int x;
        do {
            x = S[top--];
            in_stack[x] = false;
            belong[x] = Order;
        } while(x != u);
    }
}
void solve()
{
    for(int i = 1; i <= n; i++) if(!dfn[i]) Tarjan(i);
}









 

<2> 割點u的充要條件:   (1) u爲樹根,且u有多於一個子樹。 
                        (2) u不爲樹根,且滿足存在(u,v)爲樹枝邊(或稱父子邊,即u爲v在搜索樹中的父親),使得DFS(u)<=Low(v)。
    橋(u,v)的充要條件: 當且僅當(u,v)爲樹枝邊,且滿足DFS(u)<Low(v)


<3>[求雙連通分支]


雙連通分支: 圖的極大雙連通子圖。我喜歡稱之爲“塊”。
下面要分開討論點雙連通分支與邊雙連通分支的求法。


對於點雙連通分支,實際上在求割點的過程中就能順便把每個點雙連通分支求出。建立一個棧,存儲當前雙連通分支,在搜索圖時,每找到一條樹枝邊或後向邊(非橫叉邊),就把這條邊加入棧中。如果遇到某時滿足DFS(u)<=Low(v),說明u是一個割點,同時把邊從棧頂一個個取出,直到遇到了邊(u,v),取出的這些邊與其關聯的點,組成一個點雙連通分支。割點可以屬於多個點雙連通分支,其餘點和每條邊只屬於且屬於一個點雙連通分支。


對於邊雙連通分支,求法更爲簡單。只需在求出所有的橋以後,把橋邊刪除,原圖變成了多個連通塊,則每個連通塊就是一個邊雙連通分支。橋不屬於任何一個邊雙連通分支,其餘的邊和每個頂點都屬於且只屬於一個邊雙連通分支。






*** 上面方法是用棧來存邊 *** 其實也可以用棧來存點來搞的  ***
當每次dfn[u] <= low[v]時就是遇到一個割點,這個時候就從棧頂把元素一個一個取出,直到點v。注意,是點v,不是點u。因爲點u是割點,可能存在於多個雙連通分量裏面,所以它要繼續呆在棧裏面。當然,當前雙連通分量是包括u的,雖然不取出來,但還是要包括的。
所以說,其實存邊和存點是一樣的。 






























<4>[構造雙連通圖]


方法簡述:縮點,對縮之後的點建圖,統計其葉節點個數leave,則構造雙連通圖所需要的邊爲(leave+1)/2.




一個有橋的連通圖,如何把它通過加邊變成邊雙連通圖?方法爲首先求出所有的橋,然後刪除這些橋邊,剩下的每個連通塊都是一個雙連通子圖。把每個雙連通子圖收縮爲一個頂點,再把橋邊加回來,最後的這個圖一定是一棵樹,邊連通度爲1。


統計出樹中度爲1的節點的個數,即爲葉節點的個數,記爲leaf。則至少在樹上添加(leaf+1)/2條邊,就能使樹達到邊二連通,所以至少添加的邊數就是(leaf+1)/2。具體方法爲,首先把兩個最近公共祖先最遠的兩個葉節點之間連接一條邊,這樣可以把這兩個點到祖先的路徑上所有點收縮到一起,因爲一個形成的環一定是雙連通的。然後再找兩個最近公共祖先最遠的兩個葉節點,這樣一對一對找完,恰好是(leaf+1)/2次,把所有點收縮到了一起。


題目:poj_3352























































































































































































































發佈了52 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章