无向图点双连通分量(求割点)
题意:给定一个由 n 个点 m 条边构成的无向图,请你求出该图删除一个点之后,连通块最多有多少。
如果这个图是一个树的话啊,那么可以用dfs求解O(N)但是是一个图可能存在环用dfs会相当麻烦,因此利用割点来求会方便很多。对于整个图来说假如有cnt个连通块,如果一个连通块i不存在割点,那么最大值就是cnt + 1, 如果连通块存在割点,那么最大值就是cnt - 1 + x(删除割点剩余的连通块数目 + x本身算一个点);
const int N = 10010, M = 30010;
int n, m,h[N], e[M], ne[M], idx,dfn[N], low[N], timestamp,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])//没有遍历过j
{
tarjan(j);
low[u] = min(low[u], low[j]);//用j更新u
if (low[j] >= dfn[u]) cnt ++ ;//j为割点 则多一个连通块
}
else low[u] = min(low[u], dfn[j]);
}
if (u != root) cnt ++ ;// 如果u不是根节点 再把u的父节点的连通块加上
ans = max(ans, cnt);
}
int main()
{
while (cin >> n >> m, n || m)
{
memset(dfn, 0, sizeof dfn); memset(h, -1, sizeof h); idx = timestamp = 0;
while (m -- )
{
int a, b; cin >> a >> b; add(a, b), add(b, a);
}
ans = 0;//把每个点删完后 最多能分为几块
int cnt = 0;//连通块数量
for (root = 0; root < n; root ++ )
if (!dfn[root])//如果点root没搜索过 连通块数+1
cnt ++ ,tarjan(root);
cout << ans + cnt - 1 << endl;
}
}