tarjan求桥

tarjan求桥

定义

1、强连通:
在有向图G中,如果两个顶点u,v间有一条从u到v的有向路径,同时还有一条从v到u的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向非强连通图的极大强连通子图,称为强连通分量。
2、桥:
在图中,如果删除一条边使得这个图分成了两个图(没有其他边连通),我们就称这条边为桥

例题

P1656 炸铁路

题目大意:

在一个图中,炸掉一条边,使得某两个点没有路径相连。

思路:

题意要求出图上所以的桥
我们可以先设出两个数组:
1、 d f n i dfn_i dfni 表示 i i i点的时间戳(点 i i i是第几个被访问的点)
2、 l o w i low_i lowi 与点 i i i能到达所有点的时间戳的最小值(不包括 i i i的父亲
当原图上的边连接两个端点 f a t h e r 和 s o n father和son fatherson:
显然,当 d f n f a t h e r < l o w s o n dfn_{father} < low_{son} dfnfather<lowson时这条边为桥

code:

#include<bits/stdc++.h>
#define LL long long 
#define inl inline
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
using namespace std;
const int N = 155 , M = 5005;
int dfn[N] , low[N] , n , m , ct , cnt = 1 , hd[N] , flag[M << 1];
struct E {
    int to , fr , nt;
}e[M << 1];
struct Pr {
    int fr , to;
}pr[M << 1];
inl void add (int x , int y) {
    e[++cnt].to = y , e[cnt].fr = x , e[cnt].nt = hd[x] , hd[x] = cnt;
}
inl int read () {
    int val = 0, fu = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') fu = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <='9') {
        val = val * 10 + (ch - '0');
        ch = getchar ();
    }
    return val * fu;
}
inl void dfs (int x , int fa) {
    dfn[x] = low[x] = ++ct;
    int y;
    for (int i = hd[x] ; i ; i = e[i].nt) {
        y = e[i].to;
        if (!dfn[y]) {
            dfs (y , x);
            low[x] = min (low[x] , low[y]);
            if (low[y] > dfn[x]) {
                flag[i] = flag[i ^ 1] = 1;
            }
        }
        else if (y != fa) {
            low[x] = min (low[x] , dfn[y]);
        }
    }
}
inl bool comp (Pr x , Pr y) {
    if (x.fr < y.fr) return 1;
    else if (x.fr == y.fr && x.to < y.to) return 1;
    else return 0; 
}
int main () {
    int u , v;
    n = read () , m = read ();
    fu(i , 1 , m) {
        u = read () , v = read ();
        add (u , v);
        add (v , u);
    }
    fu(i , 1 , n) {
        if (dfn[i]) continue;
        dfs (i , 0);
    }
    ct = 0;
    for (int i = 2 ; i <= cnt ; i += 2) {
        int x = min (e[i].fr , e[i].to) , y = max (e[i].fr , e[i].to);
        if (flag[i]) {
            pr[++ct].fr = x , pr[ct].to = y;  
        }
    }
    sort (pr + 1 , pr + ct + 1 , comp);
    fu(i , 1 , ct) {
        printf ("%d %d\n" , pr[i].fr , pr[i].to);
    }
    return 0;
}
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值