题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4587
题意:
现在你有一个3000个点的无向连通图,现在要你删掉两个点使得,剩下的图中的连通块数量最大,输出最大的连通块个数。
做法:
这道题可真是一波三折…本来以为自己考虑的差不多了,结果发现了一个小细节的地方有大学问。。
先说为什么要用 b c c bcc bcc ,假设我们考虑只是删去一个点的情况。了解 b c c bcc bcc 的同学应该是知道的,被拎出来的割点的度即为在这个点所在的连通块中,删去这个点可得的剩余连通块个数。
这个做法是 O ( n ) O(n) O(n) 的,所以我们只要枚举所有的点进行这样的操作就好了。
但在代码中,我们会发现,有并不是割点的点也要被枚举到(这些非割点的答案是 0 0 0 或者 1 1 1 ),而且你会惊讶的发现不枚举反而是错的!!这个问题困扰了我很久…毕竟删掉割点,才能增加连通块的个数。
最后发现,因为这个图本身可能会是不连通的,所以可能会出现这样的情况, 1 − 2 , 3 − 4 1-2, 3-4 1−2,3−4, 这样的点在枚举点 1 1 1 之后,点 3 3 3 和点 4 4 4 两个中是没有割点的,包括点 2 2 2 肯定就不是了,这样的答案到最后肯定会出问题。
然后你就会想,那就和连通块的个数取个 m a x max max 就好啦,但是这样的话在图 1 − 2 , 1 − 3 , 1 − 4 1-2,1-3,1-4 1−2,1−3,1−4 中删掉点 1 1 1 的时候,再删一个点最大的连通块个数就是 2 2 2 了。
所以,我们要对所有的非删掉的点取一个最大的值,再进行删减,唉…
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int maxn=5005;
const int maxm=maxn*2+5;
int bj[maxm],belong[maxn],flag[maxm],pf[maxn];
int n,m,dfn[maxn],top,st[maxm],ef[maxm],N;
int head[maxn],to[maxm],nex[maxm],cnt,idx;
int isCut[maxn],edg[maxn],esum,low[maxn];
pii E[maxm*2];
void add(int u,int v){
to[cnt]=v,nex[cnt]=head[u],head[u]=cnt++;
}
void tarjan(int root,int fa){
dfn[root]=low[root]=++idx;
int child=0;
for(int i=head[root];~i;i=nex[i]){
int v=to[i];
if(ef[i]||flag[i]) continue;
ef[i]=ef[i^1]=1;
st[++top]=i;
if(!dfn[v]){
child++;
tarjan(v,root);
low[root]=min(low[root],low[v]);
if(dfn[root]<=low[v]){
isCut[root]=1;
N++;
while(1){
int j=st[top--];
if(bj[to[j]]!=N){
bj[to[j]]=N;
E[++esum]={to[j],N};
}
if(bj[to[j^1]]!=N){
bj[to[j^1]]=N;
E[++esum]={to[j^1],N};
}
belong[(j>>1)+1]=N;
if(i==j) break;
}
}
}
else low[root]=min(low[root],dfn[v]);
}
if(root==fa&&child<2) isCut[root]=0;
}
void init(){
rep(i,1,n) head[i]=-1;
cnt=0;
}
void fresh(){
esum=idx=N=0;
rep(i,1,n){
isCut[i]=edg[i]=bj[i]=0;
dfn[i]=low[i]=belong[i]=0;
}
for(int i=0;i<cnt;i++){
ef[i]=0;
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
init();
for(int i=1;i<=m;i++){
int x,y; scanf("%d%d",&x,&y); x++,y++;
add(x,y); add(y,x);
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=head[i];~j;j=nex[j]){
flag[j]=flag[j^1]=1;
}
pf[i]=1;
int tmp=0;
for(int j=1;j<=n;j++){
if(pf[j]||dfn[j]) continue;
tarjan(j,j);
tmp++;
}
for(int j=1;j<=esum;j++){
edg[E[j].first]++;
}
for(int j=1;j<=n;j++){
if(j!=i) ans=max(ans,tmp+edg[j]-1);
}
fresh();
for(int j=head[i];~j;j=nex[j]){
flag[j]=flag[j^1]=0;
}
pf[i]=0;
}
printf("%d\n",ans);
}
return 0;
}