tarjan 学习总结

前言 :

很多次碰到tarjan, 然而一直没学会qwq,

今天终于看懂了, 写篇总结以后复习 。

先放一篇很好的博客:mengxiang000

思想:

首先,tarjan是一种求强联通分量的算法,基于dfs序。

主要是两个重要数组:

1.dfn【i】--i点dfs到的顺序。

2.low【i】--能到达i点的最早的点 

(最早指最早遍历到,也就是dfs序最小)

low数组初始化:这个点的dfn值

那么第一个数组我们明白,第二个数组又有何用呢?慢慢来看下文证明

强联通分量一定有环(如A->C->B),从另一个分量里冲进此强联通分量,立足的第一个点设为

环头(如虚点->A)。

二:

从环头(A)一直搜到环尾(B)后,环头已经搜过,所以开始回溯。 

回溯过程包括 low【B】= min(low【B】,low【A】);(从 A 回溯到 B)

(理解:如果有一条边从u指向v,那么u的low值会与v的low值比较)

由于环头一定比环尾先搜到,1<3,所以环尾的low数组会被更新,

继续回溯,所有的环上的节点都会被更新。

三:

一直回溯(传递)到环头,环头A不会被更新(因为那就是他自己的dfn值啊qaq)

四:

此时遥望全环,难道还有谁没有臣服在环头的dfn值下?没错,环上所有点的low数组都存的是环头的dfs序,环头也是。

也就是说:low【环头】== dfn【环头】

备注(五):上句也可说明环头没被任何人更新过,也就是没有任何此联通量以外的点指向他(否则一定会被更新)

因此,我们只要判断谁的low值和dfn值相等,就能找到所有强联通分量里的大腿!

实现

计数code:

void tarjan(int x){

    vis[x] = 1;
    dfn[x] = low[x] = ++ cnt; // 初始值

    for(int i=fst[x]; i; i=nxt[i]) // 链式前向星
    {
        int v = to[i];
        if(!dfn[v]) tarjan(v); //递归
        if(vis[v]) low[x] = min(low[x], low[v]); //更新low值
    }
    if(low[x] == dfn[x])
        ans ++; //计数
}

缩点code(别人的 :)):

void tarjan(int x)
{
    low[x]=dfn[x]=++tim;
    stac[++top]=x;vis[x]=1;
    for (int i=head[x];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if (!dfn[v]) {
        tarjan(v);
        low[x]=min(low[x],low[v]);
    }
        else if (vis[v])
        {
            low[x]=min(low[x],low[v]);
        }
    }
    if (dfn[x]==low[x])
    {
        int y;
        while (y=stac[top--])
        {
            sd[y]=x;
            vis[y]=0;
            if (x==y) break;
            p[x]+=p[y];
        }
    }
}

RP ++!

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值