强连通分量-缩点-tarjan

这前几天看@liuyonglin和@steven两位巨佬在肝强连通分量,就也跟着潮流学了一下QWQ(多数都是两位大佬给讲的)

先介绍一下强连通分量:
定义:在一个有向图G(V,E)中,某子图G'(V',E')⊆G(V,E)满足:

  1. ∀a,b∈V'(a≠b)有a->b且b->a
  2. ∀G''(V'',E'')⊆G(V,E),且G''满足条件1,有V'⊄V''

    众所周知,最短路算法在有负环的时候是不成立的,而强连通分量和环是等价的我们可以在判断出强连通分量后,把它缩成一个点,然后就可以跑最短路、拓扑排序等等算法了(撒花)

    我们现在需要掌握两种算法:tarjan和kosaraju(后面一种机房网管讲过),这里主要讲一下tarjan
    blog_gaojianzhang_080e09f2e8b672be7fafe1684b0b7fd6.png
    这是一个例子,tarjan有两个时间戳dfn、low,定义dfn(u)为节点u搜索的次序编号,low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号,由定义可知,当dfn[u]=low[u]时,以u为根的搜索子树上所有节点是一个强联通分量,这里用染色的方法来区分,过程中用zhan存储,low[u]在每次返回时更新,low[u]=min(low[u],low[v])(v是从哪里返回),若u还没有被染色,且v在zhan中,还需执行low[u]=min(low[u],dfn[v]),模拟一下这个过程:
    从1开始,此时dfn[1]=low[1]=1,到3,此时dfn[3]=low[3]=2,再到5,此时dfn[5]=low[5]=3,到6,现在情况如图所示
    blog_gaojianzhang_7aacf2f611ea57166e2c6851851efe73.png
    到6的时候dfn[6]=low[6],所以以6为根的搜索子树为一个强连通分量,这里只需要退zhan退到6为止即可,发现只有一个6,颜色变成1(注意这里对于dfn等不等于low的判断是在枚举完所有边之后进行的)
    退到5,发现dfn[5]=low[5],5为一个强联通分量,颜色变成2,退到3
    blog_gaojianzhang_b3a760efd00a70d7ba2e491952d6e5f9.png
    从3到4,此时dfn[4]=low[4]=4,发现从4可以到1,1已经在zhan中,所以low[4]=min(low[4],low[1])=1
    blog_gaojianzhang_1a92e4bd99687e78f6004577da0c1685.png
    回到1,再从1到2,再从2到4,发现4在zhan中,所以low[2]=min(low[2],dfn[4])=dfn[4]=5,退回1,发现dfn[1]=low[1],退zhan到1,发现1,2,3,4为一个强连通分量,颜色为3
    blog_gaojianzhang_5605d271325a24b0f06979de70e5de9d.png
    至此tarjan就完美结束了,判断出了强联通分量,并将其染色,剩下的重建边就很简单了(帮lyl补充一下最大半连通子图的知识)

    上板子
int dfn[1005],low[1005],co[1005],num,st[1005],top,col;
struct edge{
    int l,r,v;
};
vector<edge> tmp[1005];
void tarjan(int u)
{
    dfn[u]=low[u]=++num;
    st[++top]=u;
    for(int i=0;i<tmp[u].size();i++)
    {
        int v=tmp[u][i].r;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!co[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        co[u]=++col;
        while(st[top]!=u)
        {
            co[st[top]]=col;
            top--;
        }
        top--;
    }
}

转载于:https://www.cnblogs.com/raligun/p/11175822.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值