无向图双连通分量

本文详细介绍了无向图的双连通分量(DCC),包括割点、桥、边双连通分量(e-DCC)和点双连通分量(v-DCC)的概念和性质,并提供了求解边双连通分量、点双连通分量的算法模板及典型例题,如ACwing395冗余路径、ACwing1183电力和ACwing396矿场搭建问题的解题思路。
摘要由CSDN通过智能技术生成

无向图双连通分量

1.算法分析

几个重要概念:
1.割点:无向连通图中,去掉一个顶点及其和它相邻的所有边,图中的连通分量数增加,则该顶点称为割点
2.桥(割边):无向连通图中,去掉一条边,图中的连通分量数增加,则这条边称为桥或者割边
3.双连通分量(DCC):
1)边双连通:若一个无向连通图中去掉任意一条边都不会改变此图的连通性,即不存在桥,那么称作边双连通图e-DCC
2)点双连通:若一个无向连通图去掉任意一个点都不会改变此图的连通性,即不存在割点,那么称作点双连通v-DCC

双连通分量性质:
1)边双连通分量性质:
①桥一定不在边双连通分量里面,在边双连通分量里面的一定不是桥(可以用这个性质判断桥:即记一条边e的两个端点为x,y,如果x,y在同一个dcc内,且从x->y,y先前没有被访问过(无向边相当于两条有向边),那么dfn[x]>=low[y];如果不在同一个dcc内,那么dfn[x]<low[y])
②边双连通分量内任意一条边都包含在至少一个简单环内
③边双连通分量dcc内的所有点的low[x]相同,且每个边双连通分量的根的low[root] == dfn[root]
④边双联通分量缩点后成为森林,森林中的边都是桥

2)点双连通分量性质:
①点双连通分量任意两点都同时包含在至少一个简单环内
②点双联通分量的个数一般大于等于割点数目(可能多个v-DCC共用一个割点)

算法时间复杂度
1.求边双连通分量(求桥):O(V+E)
2.求点双连通分量(求割点): O(V+E)

缩点示意图
边双缩点示意图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4zkkPc8e-1600790213822)(https://i.loli.net/2020/09/20/OcGdb78f41pUzlZ.png)]
点双缩点示意图
Image _2_.png

2. 算法模板

1.求桥、边dcc、缩点

#include<bits/stdc++.h>

using namespace std;

const int N = 5010, M = 20010;

int n, m;
int h1[N], h2[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;  // dfn记录搜索顺序,low记录回溯值(向上能够得到的最小值), timestamp记录当前搜索到的节点编号
int stk[N], top;  // stk存放dcc内的点,一个栈里的所有点属于一个dcc
int dcc[N], dccnum;  // dcc[i] = j表示i点的dcc编号为j,dccnum表示dcc的数目
bool is_bridge[M];  // is_bridge[i]判断边i是否为桥
int d[N];  // 记录每个点的度

void add(int a, int b, int h[]) {
   
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

// u:当前点,from:来的那条边
// 该算法求出桥和边dcc
void tarjan(int u, int from, int h[]) {
   
    dfn[u] = low[u] = ++ timestamp;  // 记录dfn和low
    stk[ ++ top] = u;

    for (int i = h[u]; ~i; i = ne[i]) {
   
        int j = e[i];
        if (!dfn[j]) {
   // 没有走过
            tarjan(j, i, h);
            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的话,出栈
        ++ dccnum;
        int y;
        do {
   
            y = stk[top -- ];
            dcc[y] = dccnum;
        } while (y != u);
    }
}

int main() {
   
    cin >> n >> m;
    memset(h1, -1, sizeof h1);
    memset(h2, -1, sizeof h2);

    // 建图
    while (m -- ) {
   
        int a, b;
        cin >> a >> b;
        add(a, b, h1), add(b, a, h1);
    }

    // 判断桥和边双
    for (int i = 1; i <= n; ++i) {
   
        if (!dfn[i]) tarjan(i, -1, h1);
    }

    // 缩点
    for (int i = 0; i < idx; ++i) {
   
        int a = e[i], b = e[i ^ 1];
        if (dcc[a] != dcc[b]) {
   
            add(dcc[b], dcc[a], h2);
        }
    }

    return 0;
}

2.求割点、点双、缩点

#include <bits/stdc++.h>

using namespace std;

int const N = 1e3 + 10 , M = 2e4 + 10;
int e[M], ne[M], h1[N], h2[N]<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值