eDcc(边双联通分量)缩点
在无向图中,不含割边的联通块便是边双联通分量。在边双联通分量中,每个点之间至少两条以上路径到达另外一点
在一张连通的无向图中,对于两个点u和v,如果无论删去哪条边(只能删去一条)都不能使它们不连通,我们就说u和v边双连通。
根据上一篇文章讲过关于割边的方法,这里同样缩点,记录下割边,割边的两端一定死缩点后的新点,所以用记录的割边来给新的点分配出入度,度数为1的点自然就是新树的叶子节点
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
const int N = 1e4 + 10, M = 2e5 + 10;
struct edge{
int u, v;
};
vector<edge> e;
vector<int> h[N];
int n, m;
int dfn[N], low[N], tot;
int bri[N], cnt;
int edcc[N], idx, d[N];
stack<int> stk;
void add(int a, int b) {
e.push_back({a, b});
h[a].push_back(e.size() - 1);
}
void tarjan(int x, int din) {
dfn[x] = low[x] = ++tot;
cout << "dfn[" << x << "]=" << dfn[x] << '\n';
stk.push(x);
for(int i = 0; i < h[x].size(); i++) {
int j = h[x][i], v = e[j].v;
if(!dfn[v]) {
tarjan(v, j);
low[x] = min(low[x], low[v]);
cout << "low[" << x << "]=" << low[x] << '\n';
if(low[v] > dfn[x]) {
// cout << "gebian: " << e[j].u << ' ' << e[j].v << '\n';
bri[j] = 1;
bri[j^1] = 1;
}
} else if(din != (j ^ 1)) { //这里一定要注意加括号,没加括号一直错
low[x] = min(low[x], dfn[v]);
cout << "*low[" << x << "]=" << low[x] << '\n';
}
}
if(low[x] == dfn[x]) {
cout << "eDCC:";
int y;
idx ++;
do{
y = stk.top(); stk.pop();
cout << y << ' ';
edcc[y] = idx;
}while(y != x);
cout << '\n';
}
}
int main() {
cin >> n >> m;
while(m--) {
int a, b; cin >> a >> b;
add(a, b); add(b, a);
}
tarjan(1, -1);
for(int i = 0; i < e.size(); i++) {
if(bri[i]) {
int v = e[i].v;
d[edcc[v]]++;
}
}
int ans = 0;
for(int i = 1; i <= idx; i++) {
if(d[i] == 1) ans++;
}
cout << (ans + 1) / 2; //这是一个结论,缩点后加几条边可以使得这个缩点后的树变成一个联通分量,只需记录叶子结点树,叶子结点为偶数的话,两两相连,为奇数则多出一条,所以要加ans + 1 >> 1条
return 0;
}
视频学习可以去:【D18 Tarjan eDCC 缩点】https://www.bilibili.com/video/BV1MA4y1d7CM?vd_source=4c9eb38d8205116069b961c84f64c958