题目地址:
https://www.acwing.com/problem/content/1185/
给定一个由 n n n个点 m m m条边构成的无向图,请你求出该图删除一个点之后,连通块最多有多少。
输入格式:
输入包含多组数据。每组数据第一行包含两个整数
n
,
m
n,m
n,m。接下来
m
m
m行,每行包含两个整数
a
,
b
a,b
a,b,表示
a
,
b
a,b
a,b两点之间有边连接。数据保证无重边。点的编号从
0
0
0到
n
−
1
n−1
n−1。读入以一行
0
0
0\ 0
0 0结束。
输出格式:
每组数据输出一个结果,占一行,表示连通块的最大数量。
数据范围:
1
≤
n
≤
10000
1≤n≤10000
1≤n≤10000
0
≤
m
≤
15000
0≤m≤15000
0≤m≤15000
0
≤
a
,
b
<
n
0≤a,b<n
0≤a,b<n
可以用类似求割点的Tarjan算法来做,但是本题里只需要直接求最多能产生多少个连通块就行了,不需要真的把割点都求出来。假设总共有
k
k
k个连通块,并且对于某个连通块里的某个点
u
u
u,去掉它能将其所在连通块变成
l
l
l个,那么总共个数就是
l
+
k
−
1
l+k-1
l+k−1。所以关键问题就是求
l
l
l。同求桥一样,从某个点
u
u
u开始DFS,也开两个数组dfn
和low
,dfn[v]
表示第一次走到
v
v
v的时候的时间戳,low[v]
表示从
v
v
v不经过DFS树上的点能走到的时间戳最小的点的时间戳(最后一步走到了DFS树上的点是可以的)。那么如果存在
u
↔
v
u\leftrightarrow v
u↔v这条边,并且DFS是从
u
u
u走到
v
v
v的,并且low[v] >= dfn[u]
的话,则说明
v
v
v无法绕到
u
u
u上方,那么删去
u
u
u之后,
v
v
v所在点全在
u
u
u下方的连通块就会独立出来,那么分出的连通块的个数的计数应该加
1
1
1。并且,对于
u
u
u是否是树根的情况要特判,如果
u
u
u非树根,那么
u
u
u的上方也会多出一个连通块,此时总计数要把
u
u
u上面产生的那个连通块给补上;如果
u
u
u恰好是树根的话则无此问题(这里其实考虑了
u
u
u不是割点的情况,如果
u
u
u不是割点也不是树根,那么它没有孩子能分出点全在
u
u
u下面的连通块,但是删掉
u
u
u也能产生
1
1
1个连通块)。代码如下:
#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;
int res;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void tarjan(int u, int from) {
dfn[u] = low[u] = ++timestamp;
// cnt记录的是删掉u之后,u所在连通块能分出多少个连通块
int cnt = 0;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
// v绕不到u上方,所以v能分出一个全在u下面的连通块
if (low[v] >= dfn[u]) cnt++;
} else low[u] = min(low[u], dfn[v]);
}
// u不是DFS树根,那么上面也能分出一块出来,所以cnt要加1
if (u != from) cnt++;
res = max(res, cnt);
}
int main() {
while (scanf("%d%d", &n, &m), n || m) {
memset(dfn, 0, sizeof 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);
}
// res存每个连通块在删掉枚举的点的情况下,最多能分裂成多少个小连通块
res = 0;
// 这里的cnt计算的是连通块的个数
int cnt = 0;
for (int i = 0; i < n; i++)
if (!dfn[i]) {
cnt++;
tarjan(i, i);
}
printf("%d\n", res + cnt - 1);
}
return 0;
}
时间复杂度 O ( n + m ) O(n+m) O(n+m),空间 O ( n ) O(n) O(n)。