poj2117 Electricity
试题描述
|
求一个图删除一个点之后,联通块最多有多少。
|
输入
|
多组数据。第一行两个整数 P,C 表示点数和边数。
接下来 C 行每行两个整数 p1,p2,表示 p1 与 p2 有边连接,保证无重边。读入以 0 0 结束。 |
输出
|
输出若干行,表示每组数据的结果。
|
输入示例
|
3 3
0 1 0 2 2 1 4 2 0 1 2 3 3 1 1 0 0 0 |
输出示例
|
1
2 2 |
详解参考https://blog.csdn.net/u013480600/article/details/30976823。
在dfs的时候,我们用cut[i]==X表示在dfs树中当i节点被删除时,i节点的X个儿子被切割开来(可以认为cut[i]是i节点与它的儿子连接的桥边的数目)。注意:如果i是根且其儿子只有1个,虽然i不是割点,cut[i]依然=1。如果i节点非割点,那么cut[i]=0。如果i是割点,那么cut[i]就是i被删除后将割出来的儿子数目。
然后我们求出了每个点的cut[i]值,即i点被删除,会有cut[i]个儿子树被割出来。如果i是dfs树的非根节点,那么cut[i]== 切除i之后增加的连通分量数目。如果i是dfs树的根节点,那么cut[i]-1才是切除i之后增加的连通分量数目(想想是不是)。
如果原始cut[i]=0,表示i是孤立的一点,此时cut[i]-1=-1.
如果原始cut[i]=1,表示i为根且有一个儿子,此时cut[i]-1=0.
如果原始cut[i]>=2,表示i为根且分割了>=2个儿子,此时cut[i]-1>=1.
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #define MAXN 100010 #define REP(i,k,n) for(int i=k;i<=n;i++) #define in(a) a=read() using namespace std; inline int read(){ int f=1,x=0; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } int n,m,cnt,ans; int total=0,head[MAXN],to[MAXN<<2],nxt[MAXN<<2]; int dfn[MAXN],low[MAXN],sum[MAXN]; inline void adl(int a,int b){ total++; to[total]=b; nxt[total]=head[a]; head[a]=total; return ; } inline void tarjan(int u,int f){ low[u]=dfn[u]=++cnt; for(int e=head[u];e;e=nxt[e]){ if(!dfn[to[e]]){ tarjan(to[e],u); low[u]=min(low[to[e]],low[u]); if(low[to[e]]>=dfn[u]) sum[u]++; } else if(to[e]!=f) low[u]=min(low[u],dfn[to[e]]); } return ; } int main(){ while(scanf("%d%d",&n,&m)){ if(n==0 && m==0) return 0; int a,b; total=cnt=ans=0; memset(head,0,sizeof(head)); memset(dfn,0,sizeof(head)); memset(low,0,sizeof(low)); memset(sum,0,sizeof(sum)); REP(i,1,m){ in(a);in(b); adl(a,b); adl(b,a); } int num=0; ans=-10000; REP(i,0,n-1) if(!dfn[i]){ num++; tarjan(i,-1); sum[i]--; } REP(i,0,n-1) ans=max(ans,sum[i]); cout<<ans+num<<endl; } return 0; }