1. 程式人生 > 實用技巧 >圖論學習筆記——強連通分量/雙連通分量

圖論學習筆記——強連通分量/雙連通分量

預備知識

  • 強連通:對於圖\((V,E)\)上的兩個節點\(u,v\),若存在從\(u\)\(v\)的有向邊,同時也存在從\(v\)\(u\)的有向邊,則稱這兩個點強連通。
  • 強連通圖:若圖\((V,E)\)上的任意兩個節點都強連通,則稱這個圖為強連通圖。
  • 強連通分量:有向圖的極大強連通子圖

強連通分量(SCC,Strongly Connected Components)

求強連通分量(Tarjan演算法)

\(dfn[i]:=\) 節點\(i\)在搜尋過程中的次序編號(時間戳),即記錄節點\(i\)是第幾個被搜尋到的節點。

\(low[i]:=\) 節點\(u\)及其後代節點所能追溯到的最早的節點(即祖先節點)\(v\)

的時間戳\(dfn[v]\)。當節點\(u\)第一次被搜尋到時,初始化為low[i]=dfn[i].

int dfs_clock, scc_cnt;
int dfn[maxn], low[maxn], sccno[maxn];

void dfs(int u) {
    dfn[u] = low[u] = ++dfs_clock;
    //給節點u打上時間戳
    s.push(u);
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].to;
        if (!dfn[v]) {
            //節點v還沒被搜尋到
            dfs(v);
            low[u] = min(low[u], low[v]);
            //維護祖先中最小的時間戳
        }
        else if (!sccno[v]) {
            low[u] = min(low[u], dfn[v]);
            //節點v已經被搜尋過了,但還不屬於某一個SCC
        }
    }
    //對節點u的所有後代節點完成搜尋之後
    //開始判斷節點u是不是這個強連通分量中第一個出現的節點
    if (low[u] == dfn[u]) {
        scc_cnt++;
        //SCC數量+1
        while (1) {
            int x = s.top(); s.pop();
            sccno[x] = scc_cnt;
            //給分量中的所有節點記錄所在SCC的編號
            if (x == u) break;
            //訪問完u之後,就完成了對這個SCC所有節點的訪問,跳出
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0;
    mem(sccno, 0);
    mem(dfn, 0);
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) dfs(i);
    }
}

縮點

顧名思義,將圖中的強連通分量看作是一個點,就是縮點。同時原圖變為一個DAG,如此便可以將一個有環的圖轉化為DAG,可以利用DAG的性質解決問題。

問題一:給定一個有向圖\((V,E)\),包含\(n\)個點和\(m\)條邊,問至少還要再新增多少條邊才能使整個圖變成強連通圖。

對於DAG,這個問題的答案是\(max(a,b)\),其中\(a\)是入度為零的節點個數,\(b\)是出度為零的節點個數。特別地,如果DAG中只有一個點,則答案為0。但問題中給出的圖不一定無環,此時就可以用縮點的方法將圖轉化為DAG。

問題二:給定一個有向圖\((V,E)\),包含\(n\)個點和\(m\)條邊,每個點有一個權值。求一條路徑,使得路徑上點的權值和最大。允許多次經過一條邊或一個點,但權值只計算一次。

對於DAG,這個問題就是求DAG上的最長路,用DAG上的dp即可解決。定義\(dp[i]\)為從節點\(i\)出發的路徑的最大權值和,則轉移方程為\(dp[i]=max(dp[i],dp[j]+val[i])\),其中節點\(j\)滿足:存在\(i→j\)的有向邊。

需要注意的是,在方程中需要先求出\(dp[j]\),才能用它來更新\(dp[i]\)。具體程式碼實現有兩種方法:

  1. 記憶化搜尋;
  2. 先求圖的拓撲排序,再以拓撲排序的倒序進行dp。

題目給定的圖可能有環,只需對原圖求強連通分量,縮點,建立新圖,則新圖就是DAG。