Tarjan 算法及其应用

Tarjan 算法及其应用

NO.1 求强连通分量

学习链接: https://www.cnblogs.com/shadowland/p/5872257.html

学习心得: dfn[cur] 记录访问 cur 结点的时间戳,low[cur] 记录 cur 结点及其子树中时间戳最小是多少,严格意义上来讲low[cur],记录的是在不回头遍历父节点的前提下第一次能访问到的最早的已遍历结点的时间戳。显然当访问 cur 结点的子节点 to 时,若 dfn[to] 不为0,则 to 结点到 cur 或 cur 的祖先结点中必然存在环,亦即到 to 结点为止的递归栈中的所有结点可以构成一个强连通分量。因为要先处理子节点后才能得出上层结点的 low,因此必然用后序遍历。具体的细节证明可参考链接。

画张图解释下上述low严格意义上的含义。假设现在建无向边(加边时建正向反向各建一次边),建边:6点、6条双向边
1 2
1 3
3 4
3 5
4 6
5 6
在这里插入图片描述

结果:遍历顺序1->3->5->6->4->(到3,回溯改4、6、5)->2,这里dfs序中3可看作4的虚子结点,即3在4、5、6的子树中,1、2不在。

在这里插入图片描述
代码实现:

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define mem( f, x ) memset( f, x, sizeof( f ) )
#define pii pair<int, int>
#define fi first
#define se second
#define mk(x, y) make_pair( x, y )
#define pk push_back
using namespace std;
const int M = 1e5 + 5;
const int N = 1e5 + 5;
int m, n, cnt, ck_num, col_num;
int head[N], dfn[N], low[N], color[N];
bool vis[N];
int sk[N], top;

struct eg{
   
    int to, pre;
    eg( ){
    to = pre = 0; }
    eg( int tt, int pp ){
   
        to = tt, pre = pp;
    }
}e[2*M];

void add( int x, int y ){
   
    e[++cnt] = eg( y, head[x] );
    head[x] = cnt;
}

void Tarjan( int cur ){
   
    dfn[cur] = low[cur] = ++ck_num;
    vis[cur] = 1;
    sk[++top] = cur;
    for( int i = head[cur]; i; i = e[i].pre ){
   
        int to = e[i].to;
        if( !dfn[to] ){
   
            Tarjan( to ); //后序遍历
            low[cur] = min( low[cur], low[to] ); //根据子树更新当前结点中的low[cur]
        }
        else if( vis[to] )
            low[cur] = min( low[cur], dfn[to] ); //邻接结点在递归栈中时,根据邻接结点更新当前结点的low
                                                //(已遍历至终点,终点low可直接根据其已成为祖先的邻接结点的时间戳直接更新low)
    }
    if( dfn[cur] == low[cur] ){
   
        color[cur] = ++col_num;
        vis[cur] = 0; //cur结点出栈是一定要记得
        while( sk[top] != cur ){
   
            color[sk[top]] = col_num;
            vis[sk[top--]] = 0;
        }
        top--;
    }
}

void init( ){
   
    for( int i = 0; i <= n; i++ )
        head[i] = dnf[i] = low[i] = vis[i] = color[i] = 0;
    cnt = top = ck_num = col_num = 0;
}

int main( ){
   
    while( scanf( "%d %d", &n, &m ) != EOF ){
   
        init( );
        int x, y;
        for( int i = 0; i < m; i++ ){
   
            scanf( "%d %d", &x, &y );
            add( x, y 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值