求无向图删除一个节点之后还剩下多少连通块(点的双连通分量:极大的不包含割点的连通块)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10010, M = 30010;
int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp; //dfn兼判重数组
int root; // 记录每个连通块的"根节点"
int 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 s = 0; // 如果当前点u是割点的话,去掉该点u得到的连通分量的个数
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]) // 说明u是可能是割点, u存在一棵子树(删除割点u)
s++;
} else low[u] = min(low[u], dfn[j]);
}
//如果不是根节点
/*
/
u 删掉u后 除子节点yi外
/ \ 还要要加上父节点部分+1
o o
*/
//最后还要加上父节点部分1
if (u != root) s++; // 不用加上&& s的判断,因为u不是割点的话,s要取1
ans = max(ans, s);
}
int main() {
while (scanf("%d%d", &n, &m), n || m) {
memset(dfn, 0, sizeof dfn); // 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; // 记录连通块的数目
//每次将其中联通块遍历,用tarjan
for (root = 0; root < n; root++) // 节点编号从0~n-1
if (!dfn[root]) { //dfn数组兼判重数组,求联通块的数量
cnt++;
tarjan(root);
}
printf("%d\n", cnt + ans - 1);
}
return 0;
}