【练习】POJ 2117 Electricity (图的割点)

题意

给出 n n 个点,m条边的无向图,求出删除一个点后,最多图中能形成多少个联通分量。

题解

删除点能够增加原图连通分量的是割点,所以本题就是求删除一个割点后,联通分量最大的个数。
首先使用Tarjan算法要把所有的割点找出来,找割点的时候同时记录他的孩子有多少个。但是孩子的个数并不是删除他后能够新增的连通分量个数,原因有如下几点:

  1. 若他是遍历时候的根节点,如果他有一个孩子,那么删除之后并不能增加连通分量的个数;
  2. 由于原图可能有多个连通分量,所以孩子数目要-1.

需要注意的是,当dfs树中的孩子不存在返祖边的时候,才能算是一个去除一个点后的连通分支个数。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 1e6+1000;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
int N,M;
struct edge{
    int nxt,to;
};
struct Cut_Tarjan{
    int dfn[nmax],low[nmax],cutnum[nmax],head[nmax],dfs_clock,n,scc,tot;
    bool iscut[nmax];
    edge e[nmax<<1];
    void add_edge(int u, int v){
        e[tot].to = v, e[tot].nxt = head[u];
        head[u] = tot++;
    }
    void init(int n){
        memset(head,-1,sizeof head);
        memset(dfn,0,sizeof dfn);
        memset(cutnum,0,sizeof cutnum);
        memset(iscut,0,sizeof iscut);
        dfs_clock = tot = scc = 0;
        this->n = n;
    }
    void dfs(int u,int fa){
        int child = 0;
        low[u] = dfn[u] = ++dfs_clock;
        for(int i = head[u];i!=-1;i = e[i].nxt){
            int v = e[i].to;
            if(v != fa){
                if(!dfn[v]){
                    child ++;
                    dfs(v,u);
                    low[u] = min(low[u],low[v]);
                    if(fa != -1 && low[v] >= dfn[u]) cutnum[u] ++,iscut[u] = true;
                }else if(dfn[v]<low[u]) low[u] = min(low[u],dfn[v]);
            }
        }
        if(fa == -1 && child > 1) iscut[u] = true;
        if(fa == -1) cutnum[u] = child - 1;
    }
    void start(){
        for(int i = 1;i<=n;++i) if(!dfn[i]) dfs(i,-1),scc++;
    }
}cut_t;
int main(){
    while(scanf("%d %d",&N,&M) != EOF){
        if(N == 0 && M == 0) break;
        cut_t.init(N);
        int u,v;
        for(int i = 1;i<=M;++i){
            scanf("%d %d",&u,&v);
            cut_t.add_edge(u+1,v+1);
            cut_t.add_edge(v+1,u+1);
        }
        cut_t.start();
        int ans = 0;
        for(int i = 1;i<=N;++i) ans = max(ans,cut_t.scc + cut_t.cutnum[i]);
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值