题目描述
求一个无向图图删除一个点之后,连通块最多有多少。
输入输出格式
输入格式:
多组数据。第一行两个整数 P,C 表示点数和边数。
接下来 C 行每行两个整数 p1,p2,表示 p1 与 p2 有边连接,保证无重边。读入以 `0 0` 结束。
输出格式:
输出若干行,表示每组数据的结果。
输入输出样例
输入样例#1:
3 3
0 1
0 2
2 1
4 2
0 1
2 3
3 1
1 0
0 0
输出样例#1:
1
2
2
数据范围
1 < = P < = 10000,C > = 0,0 < = p1, p2 < P
题目分析
显然,要在所有连通块中寻找一点,使得删掉其后分出的点更多。
记连通块总数为ans,一个点删去后将该连通块分成k块。则答案为。
将图以dfs求得时间戳后,对于一点,若非根节点,则能分出其孩子数+1个连通块(当然,满足low[v]>=dfn[u])根节点由于没有父亲,不用+1。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=10010,MAXM=100010;
int n,m,cnt=0;
int head[MAXN];
struct Edge{
int to;
int next;
}edge[MAXM];
void add_edge(int u,int v)
{
edge[++cnt]=(Edge){v,head[u]};
head[u]=cnt;
}
int dfn[MAXN],ans=0,low[MAXN],tot=0,k=0,vis[MAXN];
void tarjan(int u,int G)
{
low[u]=dfn[u]=++tot;
int c=0,ch=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!dfn[v])
{
ch++;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]&&u!=G) c++;
}
else low[u]=min(low[u],dfn[v]);
}
if(c)k=max(k,c+1);
if(u==G)k=max(k,ch);
}
int f[MAXN];
int find(int kk)
{
if(f[kk]==kk) return kk;
else return f[kk]=find(f[kk]);
}
int main()
{
while(cin>>n>>m)
{
if(!n&&!m) break;
memset(head,-1,sizeof(head));
cnt=0;ans=0;tot=0;k=0;
memset(dfn,0,sizeof(dfn));
memset(vis,0,sizeof(vis));
memset(low,0,sizeof(low));
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
x++,y++;
add_edge(x,y);
add_edge(y,x);
if(find(x)!=find(y)) f[find(x)]=find(y);
}
for(int i=1;i<=n;i++)
{
if(!vis[find(i)])
{
vis[find(i)]=1;
ans++;
}
}
for(int i=1;i<=n;i++)if(!dfn[i]) tarjan(i,i);
cout<<ans+k-1<<endl;
}
return 0;
}