边的双连通分量——冗余路径

人生重开入口:395. 冗余路径 - AcWing题库

思路:题意就是给定一个图,问至少要加几条边才能把图变成一个边双连通分量,不含桥。

利用tarjan算法,缩完点后原图会变成一颗树(不是拓扑图),对于所有的叶节点,都至少要加一条边连向树中的其他节点。如果每次都连另一个叶子节点,那么最后要连的边的数目就是(cnt+1)/2,cnt是叶节点的数量。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const  int N=5e3+10,M=2e4+10;
int n,m;
int h[N],e[M],ne[M],idx;
int dfn[N],low[N],times; //两个时间戳
int stk[N],top;
int id[N],dcc_cnt; //双连通分量编号
bool is_b[N]; //改边是否是桥
int d[N];  //记录该点度数
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u,int father)//记录来边,防止搜索反向边
{
    dfn[u]=low[u]=++times;
    stk[++top]=u;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(!dfn[j])
        {
            tarjan(j,i);
            low[u]=min(low[u],low[j]);
            if(low[j]>dfn[u]) //若节点j永远走不到节点u,则为桥边
            {
                is_b[i]=is_b[i^1]=true;  //反向边也是桥边
            //加边时是两条边一起加的,所以同一路径的正向边和反向边总是同时出现。
            //奇数异或1就是减1操作,偶数异或1就是加1操作
            }
        }else if(i!=(father^1)) //不能遍历回父节点。
        low[u]=min(low[u],dfn[j]); //u的low值就是u不通过父节点能走到的节点的最小dfn值
    }
    if(dfn[u]==low[u]) //和强连通分量一样,当前节点能走到的最小时间戳是自己的话,则该点是边连通分量的最高点
    {
        ++dcc_cnt;
        int  y;
        do{
            y=stk[top--];
            id[y]=dcc_cnt;
        }while(y!=u);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    tarjan(1,-1);

    for(int  i=0;i<idx;i++) //idx记录的是边的数量,
    {
        if(is_b[i])    //边i是桥边的话,让它出边所在边连通分量度数加1,反向边加的是另一个方向的边连通分量的度数
        {
            d[id[e[i]]]++;
        }
    }
    int sum=0;
    for(int i=1;i<=dcc_cnt;i++)//遍历所有的边连通分量,度数为1说明是叶子节点。
    if(d[i]==1)
        sum++;

    printf("%d\n",(sum+1)/2);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值