Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释

   原题链接

    无向连通图中,如果删除某边后,图变成不连通,则称该边为桥。

  也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点;然后再把所有点遍历一遍, 看其low和dfn,满足dfn[ fa ]<low[ i ](0<i<=n, i 的 father为fa) —— 则桥为fa-i。 找桥的时候,要注意看有没有重边;有重边,则不是桥。

  另外,本题的题意及测试样例中没有重边,所以不用考虑重边。

  


 

带详细注释的题解:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<vector>
#define maxn 10010
using namespace std;
int dfn[maxn],low_link[maxn] ,Father[maxn];
//tarjan 算法的dfn ——在DFS过程中 的访问序号(也可以叫做开始时间
//tarjan 算法的low_link[i]——从i节点出发DFS过程中i下方节点所能到达的最早的节点的 开始时间
int bridgenum, Time ,n ;  //桥的总数,dfn时间戳,n为顶点数,
vector<int>G[maxn]; //定义图的邻接矩阵表
stack<int>st;
struct node{
    int u,v;
}bridge[maxn];  //整个图的桥的存储
bool cmp( node a,node b )
{
    if(a.u!=b.u)return a.u<b.u;
    else return a.v<b.v;
}
void init(){
    int i;
    for(i=0;i<=n;i++)  //初始化邻接表
        G[i].clear();
    bridgenum=0;Time=0;
    memset(dfn,0,sizeof(dfn));
    memset(low_link,0,sizeof(low_link));
    memset(Father,0,sizeof(Father));
}
void tarjan(int u,int fa)
{
    low_link[u]=dfn[u]=++Time;
    Father[u]=fa; //记录父节点
   // st.push(u);
    for(int i=0;i<(int)G[u].size();i++){
        int v=G[u][i];
        if(!dfn[v]){
            tarjan(v,u);
            low_link[u]=min(low_link[u],low_link[v]);
        }
        else if(v!=fa){  //不能连接到父节点!
            low_link[u]=min(low_link[u],dfn[v]);
        }
        else{
            //这种情况就是有重边的情况!不予处理,直接跳过!
        }
    }
}
void solve()
{
    for(int i=0;i<n;i++){
        if(!dfn[i])
            tarjan(i,-1);
    }
    int ans=0;
    for(int i=0;i<n;i++){
        int v=Father[i];
        if(dfn[v]<low_link[i]&&v!=-1){  //若v-i可以构成父节点
            bridge[ans].u=v; //桥的两条边
            bridge[ans].v=i;
            if(bridge[ans].u>bridge[ans].v)
                swap(bridge[ans].u,bridge[ans].v);
            ans++;
        }
    }
    sort(bridge,bridge+ans,cmp);
    printf("%d critical links\n",ans);
    for(int i=0;i<ans;i++){
        printf("%d - %d\n",bridge[i].u,bridge[i].v);
    }
}
int cal_num(char ch[]){
    int len=strlen(ch),s=0;
    for(int i=1;i<=len-2;i++){
        s=s*10+ch[i]-'0';
    }
    return s;
}
int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        init();
        char ch[10];
        int m ,u,v; //边数
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%s",&u,ch);
            m=cal_num(ch);  //截取出数字存入m——边数
            for(int j=1;j<=m;j++){
                scanf("%d",&v);
                G[u].push_back(v);  //这里按单向边任意一边存储就可以了,毕竟是无向图!
                G[v].push_back(u);
            }
        }
        printf("Case %d:\n",++cas);
        solve();
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/zhazhaacmer/p/8393391.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值