hdu 4587 TWO NODES 点bcc

86 篇文章 0 订阅

题目链接: 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 1234, 这样的点在枚举点 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 121314 中删掉点 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值