• acwing算法提高之图论--无向图的双连通分量


    1 介绍

    本博客用来记录无向图的双连通分量的相关题目。

    以下所有概念都是针对无向图而言的。
    :本质是边,去掉它,图就不连通了。这样的边叫作桥。
    边双连通分量:不包含桥的连通块,且边的数目最大。

    割点:本质是结点,去掉它(即去掉这个结点和它所关联的边),图就不连通了。这样的结点叫作割点。
    点双连通分量:不包含割点的连通块,且结点的数目最大。

    边双连通分量的求解方法:引入时间戳,与有向图强连通分量的求解方法类似。

    结论1:对于有向图,至少需要加多少条边,能将此图变成一个强连通分量。答案是max(p, q),其中p是起点个数(即入度为0的结点个数),q是终点个数(即出度为0的结点个数)。

    结论2:对于无向图,至少需要加入多少条边,能将此图变成一个边双连通分量。答案是(cnt + 1) / 2。其中cnt是指出度和入度皆为为1的结点数目。

    点双连通分量的求解方法

    2 训练

    题目1395冗余路径

    C++代码如下,

    #include 
    #include 
    #include 
    
    using namespace std;
    
    const int N = 5010, M = 20010;
    
    int n, m;
    int h[N], e[M], ne[M], idx;
    int dfn[N], low[N], timestamp;
    int stk[N], top;
    int id[N], dcc_cnt;
    bool is_bridge[M];
    int d[N];
    
    void add(int a, int b) {
        e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    void tarjan(int u, int from) {
        dfn[u] = low[u] = ++timestamp;
        stk[++top] = u;
        
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if (!dfn[j]) {
                tarjan(j, i);
                low[u] = min(low[u], low[j]);
                if (dfn[u] < low[j]) {
                    is_bridge[i] = is_bridge[i ^ 1] = true;
                }
            } else if (i != (from ^ 1)) {
                low[u] = min(low[u], dfn[j]);
            }
        }
        
        if (dfn[u] == low[u]) {
            ++dcc_cnt;
            int y;
            do {
                y = stk[top--];
                id[y] = dcc_cnt;
            } while (y != u);
        }
    }
    
    int main() {
        cin >> n >> m;
        memset(h, -1, sizeof h);
        while (m--) {
            int a, b;
            cin >> a >> b;
            add(a, b), add(b, a);
        }
        
        tarjan(1, -1);
        
        for (int i = 0; i < idx; ++i) {
            if (is_bridge[i]) {
                d[id[e[i]]]++;
            }
        }
        
        int cnt = 0;
        for (int i = 1; i <= dcc_cnt; ++i) {
            if (d[i] == 1) {
                cnt++;
            }
        }
        
        printf("%d\n", (cnt + 1) / 2);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    题目21183电力

    C++代码如下,

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    const int N = 10010, M = 30010;
    
    int n, m;
    int h[N], e[M], ne[M], idx;
    int dfn[M], low[N], timestamp;
    int root, ans;
    
    void add(int a, int b) {
        e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    void tarjan(int u) {
        dfn[u] = low[u] = ++timestamp;
        
        int cnt = 0;
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if (!dfn[j]) {
                tarjan(j);
                low[u] = min(low[u], low[j]);
                if (low[j] >= dfn[u]) cnt++;
            } else {
                low[u] = min(low[u], dfn[j]);
            }
        }
        
        if (u != root) cnt++;
        
        ans = max(ans, cnt);
        
        return;
    }
    
    int main() {
        while (scanf("%d%d", &n, &m), n || m) {
            memset(dfn, 0, sizeof dfn);
            memset(h, -1, sizeof h);
            idx = timestamp = 0;
            
            while (m--) {
                int a, b;
                scanf("%d%d", &a, &b);
                add(a, b), add(b, a);
            }
            
            ans = 0;
            int cnt = 0;
            for (root = 0; root < n; root++) {
                if (!dfn[root]) {
                    cnt++;
                    tarjan(root);
                }
            }
            
            printf("%d\n", ans + cnt - 1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    题目3396矿场搭建

    C++代码如下,

    #include 
    #include 
    #include 
    #include 
    
    using namespace std;
    
    typedef unsigned long long ULL;
    
    const int N = 1010, M = 1010;
    
    int n, m;
    int h[N], e[M], ne[M], idx;
    int dfn[N], low[N], timestamp;
    int stk[N], top;
    int dcc_cnt;
    vector<int> dcc[N];
    bool cut[N];
    int root;
    
    void add(int a, int b) {
        e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    void tarjan(int u) {
        dfn[u] = low[u] = ++ timestamp;
        stk[++top] = u;
        
        if (u == root && h[u] == -1) {
            dcc_cnt ++;
            dcc[dcc_cnt].push_back(u);
            return;
        }
        
        int cnt = 0;
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if (!dfn[j]) {
                tarjan(j);
                low[u] = min(low[u], low[j]);
                if (dfn[u] <= low[j]) {
                    cnt++;
                    if (u != root || cnt > 1) cut[u] = true;
                    ++dcc_cnt;
                    int y;
                    do {
                        y = stk[top--];
                        dcc[dcc_cnt].push_back(y);
                    } while (y != j);
                    dcc[dcc_cnt].push_back(u);
                }
            } else {
                low[u] = min(low[u], dfn[j]);
            }
        }
    }
    
    int main() {
        int T = 1;
        while (cin >> m, m) {
            for (int i = 1; i <= dcc_cnt; ++i) dcc[i].clear();
            idx = n = timestamp = top = dcc_cnt = 0;
            memset(h, -1, sizeof h);
            memset(dfn, 0, sizeof dfn);
            memset(cut, 0, sizeof cut);
            
            while (m--) {
                int a, b;
                cin >> a >> b;
                n = max(n, a), n = max(n, b);
                add(a, b), add(b, a);
            }
            
            for (root = 1; root <= n; ++root) {
                if (!dfn[root]) {
                    tarjan(root);
                }
            }
            
            int res = 0;
            ULL num = 1;
            for (int i = 1; i <= dcc_cnt; ++i) {
                int cnt = 0;
                for (int j = 0; j < dcc[i].size(); ++j) {
                    if (cut[dcc[i][j]]) {
                        cnt++;
                    }
                }
                
                if (cnt == 0) {
                    if (dcc[i].size() > 1) res += 2, num *= dcc[i].size() * (dcc[i].size() - 1) / 2;
                    else res++;
                } else if (cnt == 1) res++, num *= dcc[i].size() - 1;
            }
            
            printf("Case %d: %d %llu\n", T++, res, num);
        }
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
  • 相关阅读:
    MySQL·SQL优化
    JavaScript公共组件父子依赖调用及子校验父条件问题解决
    Zookeeper技术精华带领深入详细了解
    外包就干了2个月,技术退步明显....
    太好用了!MySQL8的150高效技巧,你不会还不知道吧?
    【超全汇总】HTTP协议
    程序员应该专注技术还是转管理?
    利用消球差透镜对各种偏振光束进行深聚焦
    如何把 SAP ABAP 系统里一张数据库表的内容,显示在 Adobe PDF Form 里
    水儿的绘画——dfs连通块+暴力枚举
  • 原文地址:https://blog.csdn.net/YMWM_/article/details/138008312