Tarjan(割点) - Electricity - POJ 2117

Tarjan(割点) - Electricity - POJ 2117

题意:

给定一个由 n 个点 m 条边构成的无向图,请你求出该图删除一个点之后,连通块最多有多少。

输入格式

输入包含多组数据。

每组数据第一行包含两个整数 n,m。

接下来 m 行,每行包含两个整数 a,b,表示 a,b 两点之间有边连接。

数据保证无重边。

点的编号从 0 到 n−1。

读入以一行 0 0 结束。

输出格式

每组数据输出一个结果,占一行,表示连通块的最大数量。

数据范围

1 ≤ n ≤ 10000 , 0 ≤ m ≤ 15000 , 0 ≤ a , b < n 1≤n≤10000, 0≤m≤15000, 0≤a,b<n 1n10000,0m15000,0a,b<n

输入样例:

3 3
0 1
0 2
2 1
4 2
0 1
2 3
3 1
1 0
0 0

输出样例:

1
2
2

分析:

目 标 即 在 所 有 连 通 分 量 中 选 择 一 个 割 点 , 使 得 删 除 该 点 后 分 出 的 连 通 块 数 量 最 多 。 目标即在所有连通分量中选择一个割点,使得删除该点后分出的连通块数量最多。 使

如何判断割点:

条 件 : d f n [ u ] ≤ l o w [ j ] 。 条件:dfn[u]≤low[j]。 dfn[u]low[j]

① 、 若 u 不 是 根 节 点 , 删 除 u 以 后 , u 的 父 节 点 所 在 连 通 块 与 孩 子 节 点 所 在 连 通 块 将 分 成 多 个 部 分 。 此 时 u 是 割 点 。 ①、若u不是根节点,删除u以后,u的父节点所在连通块与孩子节点所在连通块将分成多个部分。此时u是割点。 uuuu

情 况 ① 如 下 图 : 情况①如下图:
在这里插入图片描述
删 除 u 后 , 会 分 成 1 , 2 , 3 三 个 连 通 分 量 。 删除u后,会分成1,2,3三个连通分量。 u123

② 、 若 u 是 根 节 点 , 那 么 u 至 少 有 两 个 孩 子 , 才 能 确 保 u 是 割 点 。 若 u 仅 有 一 个 孩 子 , 删 去 u 后 , 连 通 分 量 数 量 不 增 加 。 ②、若u是根节点,那么u至少有两个孩子,才能确保u是割点。若u仅有一个孩子,删去u后,连通分量数量不增加。 uuuuu

情 况 ② 如 下 图 : 情况②如下图:
在这里插入图片描述

对条件的说明:

d f n [ u ] ≤ l o w [ j ] 。 dfn[u]≤low[j]。 dfn[u]low[j]

等 号 是 可 以 取 的 。 取 等 时 u 与 j 或 j 的 孩 子 之 间 形 成 环 , 删 去 u 仍 然 会 与 j 分 开 。 等号是可以取的。取等时u与j或j的孩子之间形成环,删去u仍然会与j分开。 ujjuj

d f n ] [ u ] > l o w [ j ] 时 , 即 u 在 环 的 内 部 , 删 去 u 就 不 能 够 增 加 连 通 分 量 的 数 量 。 dfn][u]>low[j]时,即u在环的内部,删去u就不能够增加连通分量的数量。 dfn][u]>low[j]uu

具体步骤:

这 样 我 们 对 于 连 通 块 中 的 每 个 点 u , 统 计 满 足 d f n [ u ] ≤ l o w [ j ] 的 孩 子 j 的 数 量 。 这样我们对于连通块中的每个点u,统计满足dfn[u]≤low[j]的孩子j的数量。 udfn[u]low[j]j

有 一 个 j , 就 能 够 多 分 出 一 个 孩 子 连 通 分 量 。 有一个j,就能够多分出一个孩子连通分量。 j

注 意 , 若 u 不 是 根 节 点 , 最 后 还 需 要 增 加 一 个 父 节 点 所 在 的 连 通 分 量 。 注意,若u不是根节点,最后还需要增加一个父节点所在的连通分量。 u

用 全 局 变 量 a n s 来 更 新 去 除 每 个 点 能 够 分 出 的 连 通 块 数 量 的 最 大 值 。 用全局变量ans来更新去除每个点能够分出的连通块数量的最大值。 ans

记 连 通 块 总 数 初 始 为 c n t , 最 大 答 案 为 a n s + ( c n t − 1 ) 。 记连通块总数初始为cnt,最大答案为ans+(cnt-1)。 cntans+(cnt1)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=10010, M=30010;

int n,m;
int e[M],ne[M],h[N],idx;
int dfn[N],low[N],timestamp;
int root,ans;

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void tarjan(int u)
{
    dfn[u]=low[u]=++timestamp;

    int res=0;  //统计割点能够将连通块分成几部分
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(!dfn[j])
        {
            tarjan(j);
            low[u]=min(low[u],low[j]);
            if(low[j]>=dfn[u]) res++;
        }
        else low[u]=min(low[u],dfn[j]);
    }
    
    if(u!=root) res++;
    
    ans=max(ans,res);
}


int main()
{
    while(~scanf("%d%d",&n,&m),n||m)
    {
        memset(h,-1,sizeof h);
        memset(dfn,0,sizeof dfn);
        idx=0,ans=0,timestamp=0;
        
        int a,b;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            add(a,b),add(b,a);
        }
        
        int cnt=0;  //统计有多少连通分量
        for(root=0;root<n;root++)
            if(!dfn[root])
            {
                tarjan(root);
                cnt++;
            }
                
        printf("%d\n",ans+cnt-1);
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值