連通圖小結

連通圖小結 A Summary for Connected Graph

Ⅰ.概念

  • 強連通
    • 強連通:有向圖中(u,v) 存在uv, vu 的兩條路徑,稱(u,v) 爲強連通
    • 強連通圖:有向圖中任意兩個頂點強連通
    • 強連通分量:有向圖的極大強連通子圖
  • 弱連通
    • 弱連通:無向圖中(u,v) 存在uv 的路徑,稱(u,v) 爲弱連通
    • 弱連通圖:無向圖中任意兩個頂點弱連通,(可以將忽略方向的有向圖看作無向圖)
    • [弱]連通分量:無向圖的極大[弱]連通子圖
  • 割點與橋
    • 割點:無向圖中點u ,刪去後連通分量數目增加(連通圖則使得圖不再連通),稱u 爲割點
    • 橋:無向圖中邊(u,v) ,刪去後連通分量數目增加(連通圖則使得圖不再連通),稱(u,v) 爲橋(割邊)
  • 點雙連通
    • 點雙連通:無向圖中(u,v) 存在至少兩條“點不重複”的路徑,稱(u,v) 爲點雙連通
      這個要求等價於任意兩條邊都在同一個簡單環中,即內部無割點
    • 點雙連通分量:無向圖的極大點雙連通子圖。
    • 性質:
      • 無向圖每條邊恰好屬於一個點雙連通分量
      • 不同的點雙連通分量之間可能會有公共點,最多隻有一個且一定是割點
      • 任意割點都是至少兩個不同點雙連通分量的公共點
  • 邊雙連通
    • 邊雙連通:無向圖中(u,v) 存在至少兩條“邊不重複”的路徑,稱(u,v) 爲邊雙連通
      這個要求低一點,只需要每條邊都至少在一個簡單環中,即所有的邊都不是橋
    • 邊雙連通分量:無向圖的極大邊雙連通子圖。
    • 性質:
      • 除橋不屬於任何邊雙連通分量外,其他每條邊恰好屬於一個邊雙連通分量
      • 把所有橋刪除後,每個連通分量對應原圖的一個邊雙連通分量

Ⅱ.連通圖算法

  • 連通分量 Connected Component
    求解連通分量只需要dfs 或者bfs 把圖搜一遍就好了,當然並查集也是可以的。
    一般不爆棧的情況,dfs 好寫適用性強
//Connected Components
vector<int> G[N];
int id[N], cc;

void dfs(int u) {
    id[u] = cc;
    for(int v : G[u]) {
        if(id[v]) continue;
        dfs(v);
    }
}

void findCC() {
    cc = 0;
    for(int i = 1; i <= n; ++i) {
        if(id[i]) continue;
        ++cc;
        dfs(i);
    }
}
  • 割點 Cut
    基於dfs 時間戳的Tarjan 算法
    dfn[u]:= u 的時間戳,low[u]:= u 及其後代能連回的最早的祖先的時間戳
    如果low[v]dfn[u] ,即v 及其所在的子樹都沒有後向邊(back edge) 連回u的祖先
    如果刪去u ,那麼u 的祖先與v 及其所在的子樹不連通,根據“連通”關係的傳遞性,整個圖就不連通了
//Tarjan-cut
vector<int> G[N];
int dfn[N], low[N], cut[N], dfsNum;

void tarjan(int u, int f) {
    dfn[u] = low[u] = ++dfsNum;
    int son = 0;
    for(int v : G[u]) {
        if(v == f) continue;
        if(!dfn[v]) {
            ++son;
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u]) cut[u] = true;
        } else low[u] = min(low[u], dfn[v]);
    }
    if(f < 0 && son == 1) cut[u] = false;
}

void init() {
    dfsNum = 0;
    memset(dfn, 0, sizeof dfn);
    memset(cut, false, sizeof cut);
    tarjan(root, -1);
}
  • Bridge
    基於dfs 時間戳的Tarjan 算法
    dfn[u]:= u 的時間戳,low[u]:= u 及其後代能連回的最早的祖先的時間戳
    如果low[v]>dfn[u] ,即v 及其所在的子樹只能連回v 自己
    如果刪去(u,v) ,那麼uv 及其所在的子樹不連通,根據“連通”關係的傳遞性,整個圖就不連通了
//Tarjan-bridge
vector<int> G[N];
vector<pair<int, int> > bridge;
int dfn[N], low[N], dfsNum;

void tarjan(int u, int f) {
    dfn[u] = low[u] = ++dfsNum;
    for(int v : G[u]) {
        if(v == f) continue;
        if(!dfn[v]) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u]) bridge.push_back({u, v});
        } else low[u] = min(low[u], dfn[v]);
    }
}

void init() {
    dfsNum = 0;
    bridge.clear();
    memset(dfn, 0, sizeof dfn);
    tarjan(root, -1);
}
  • 點雙連通分量 Biconnected Component or Block
    基於dfs 時間戳的Tarjan 算法
    dfn[u]:= u 的時間戳,low[u]:= u 及其後代能連回的最早的祖先的時間戳
vector<int> G[N];

int dfn[N], low[N], cut[N], bcc, dfsNum;
vector<int> block[N];
int stk[N], top;

void tarjan(int u, int f) {
    dfn[u] = low[u] = ++dfsNum;
    stk[++top] = u;
    int son = 0;
    for(int v : G[u]) {
        if(v == f) continue;
        if(!dfn[v]) {
            ++son;
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u]) {
                cut[u] = true;
                block[++bcc].push_back(u);
                while(true) {
                    int x = stk[top--];
                    block[bcc].push_back(x);
                    if(x == v) break;
                }
            }
        } else low[u] = min(low[u], dfn[v]);
    }
    if(f < 0 && son == 1) cut[u] = false;
}

void init() {
    bcc = dfsNum = 0;
    memset(dfn, 0, sizeof dfn);
    memset(cut, 0, sizeof cut);
    tarjan(root, -1);
}
  • 邊雙連通分量 Edgebiconnected Component
    基於dfs 時間戳的Tarjan 算法
    dfn[u]:= u 的時間戳,low[u]:= u 及其後代能連回的最早的祖先的時間戳
    邊雙連通縮點其實就是留下橋,把所有橋邊構成了新圖,新圖是一棵樹
//Tarjan-bcc
vector<int> G[N];
int dfn[N], low[N], in[N], id[N], bcc, dfsNum;
int stk[N], top;

void tarjan(int u, int f) {
    dfn[u] = low[u] = ++dfsNum;
    stk[++top] = u;
    in[u] = true;
    for(int v : G[u]) {
        if(v == f) continue;
        if(!dfn[v]) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
        } else low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u]) {
        ++bcc;
        while(true) {
            int v = stk[top--];
            in[v] = false;
            id[v] = bcc;
            if(v == u) break;
        }
    }
}

void init() {
    bcc = dfsNum = 0;
    memset(dfn, 0, sizeof dfn);
    tarjan(root, -1);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章