无向图双连通分量
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)]
点双缩点示意图
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]<