【POJ2117】Electricity-点双连通分量

测试地址:Electricity
题目大意:给定一个无向图(注意:不一定连通),问从里面去掉一个顶点以及与其相连的所有边之后,整个图被划分成的连通块的最大数目。
做法:本题需要用到点双连通分量的知识。
什么叫点双连通分量呢?首先,一个无向图是点双连通的,就代表将这个图中任意一点去掉,剩下的图仍能连通,那么类比强连通和强连通分量的定义,不难得出点双连通分量的定义:对一个无向图 G 的两个子图G0,若 G0 是点双连通的,而包含 G0 的任意其他的 G 的子图都不是点双连通的,那么G0就是图 G 的一个点双连通分量。求点双连通分量可以使用类似强连通分量的Tarjan算法来做,这个可以自己去查资料,这里就不细说了。
定义了解之后,对于这一题我们分析一下,一般来说一个顶点只从属于一个点双连通分量,而割点可以从属于多个点双连通分量,我们知道将割点从图中去掉之后,图就会被分为多个连通块,而被分成的连通块的数目正好就是这个割点从属的点双连通分量的数目。那么怎么算出一个割点从属的点双连通分量数目呢?很简单,假设点u在DFS树中的儿子节点 v low[v]中,大于等于 dfn[u] 的有 s 个,那么这个点就从属于s+1个点双连通分量。特别地,对于DFS树的根,它只从属于 s 个点双连通分量。这个应该很容易看得出来了,因为我们判断割点的时候,一旦出现满足上述条件的情况,这个点就是割点,同时也可以作为一个点双连通分量的根,那么如果上述情况出现了s次,这个点就可以作为 s 个点双连通分量的根,而这个点不作为点双连通分量根的情况也有一种(和它在DFS树中的父亲节点同属一个点双连通分量),注意这种情况当该点为DFS树的根时是不存在的,所以答案对于非根节点就是s+1,对于根节点是 s 。那么我们只需要统计每个点所从属的点双连通分量数目的最大值,这个最大值就是答案。
然而本题还没有结束,注意到原图可能不连通,那么设原图有cnt个连通块,上述求出的答案是 ans ,显然正确的答案是 ans+cnt1 。这样我们就解决了这个问题。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,tot,tim,ans,rt,first[10010],dfn[10010],low[10010];
struct edge {int v,next;} e[2000010];
bool vis[10010];

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void dfs(int v)
{
    vis[v]=1;
    dfn[v]=++tim;
    low[v]=dfn[v];
    int s=1;
    for(int i=first[v];i;i=e[i].next)
    {
        if (!vis[e[i].v])
        {
            dfs(e[i].v);
            if (low[e[i].v]>=dfn[v]) s++;
            low[v]=min(low[v],low[e[i].v]);
        }
        else low[v]=min(low[v],dfn[e[i].v]);
    }
    if (v!=rt) ans=max(ans,s);
    if (v==rt) ans=max(ans,s-1);
}

void tarjan()
{
    memset(vis,0,sizeof(vis));
    tim=0;
    int cnt=0;
    for(int i=0;i<n;i++)
        if (!vis[i])
        {
            cnt++;
            rt=i;
            dfs(i);
        }
    ans+=cnt-1;
}

int main()
{
    while(scanf("%d%d",&n,&m)&&n)
    {
        memset(first,0,sizeof(first));
        tot=0;
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            insert(a,b),insert(b,a);
        }
        ans=0;
        tarjan();
        printf("%d\n",ans);
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值