• Tarjan在图论中的应用(一)——用Tarjan来实现强连通分量缩点


    前言

    (Tarjan)是一个著名的将强连通分量缩点的算法。

    大致思路

    它的大致思路就是在图上每个联通块中任意选一个点开始进行(Tarjan)操作(依据:强连通分量中的点可以两两到达,因此从任意一个点开始都没关系)。

    具体实现

    对于每一个点,先记录它的(dfs),并将该点加入一个栈中,并标记其在栈中,然后用(low[])数组来记录从它出发能到达的字典序最小的节点。

    枚举它所能到达的每一个节点,并对每一个节点进行分类讨论:

    设当前节点为(x),枚举到的节点为(son)

    如果(son)没有被访问过,就先对它进行Tarjan操作,然后更新(low[x])(low[x]=min(low[x],low[son])))。

    如果(son)已经被访问过,又分两种情况:

    • 如果(son)在栈中,那么更新(low[x])

    • 如果(son)不在栈中,那么代表已经对(son)所能到达的每一个节点操作过,说明从(son)不能到达(x),即它们不在同一个强连通分量中,因此不能更新(low[x])

    在枚举完每一个节点后,我们可以判断当前节点是否就是它能到达的dfs序最小的节点,如果是的话,说明它是一个强连通分量中最早被访问过的(当然,也有可能说明它所在的强连通分量中就只有它一个节点),否则,就说明还有比它更早被访问过的,那么退出函数。

    如果当前节点是一个强连通分量中最早被访问到的,那么就说明栈中在它上面的节点全都和它在一个强连通分量中,我们可以新建一个强连通分量,并将它连同在它上面的点全部加入这个强连通分量中即可(加入的同时要注意更新这个强连通分量的信息,可参考例题)。

    代码

    inline void Tarjan(int x)//x是当前访问到的节点
    {
        dfn[x]=low[x]=++d,Stack[++top]=x,vis[x]=1;//记录当前节点的dfs序与当前节点所能到达的dfs序最小的点,将当前节点加入栈中,并标记当前节点在栈中
        for(register int i=lnk[x];i;i=e[i].nxt)//枚举从当前节点出发的每一条边
        {
            if(!dfn[e[i].to]) Tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]);//如果这个节点没访问过,就先对这个节点进行操作,然后更新当前节点能到达的dfs序最小的点
            else if(vis[e[i].to]) low[x]=min(low[x],low[e[i].to]);//否则,如果这个点在栈中,就进行更新
        }
        if(low[x]==dfn[x])//如果当前节点就是当前节点能到达的dfs序最小的点,则对当前强连通分量进行缩点
        {
            a[x].col=++cnt,vis[x]=0;//给当前节点加入一个新的强连通分量,并标记当前节点已出栈(如果需要,还要初始化这个强连通分量的信息,可参考例题)
            while(Stack[top]^x) a[Stack[top]].col=cnt,vis[Stack[top--]]=0;//将栈中当前节点之上的节点一一弹出(如果需要,还要同时更新这个强连通分量的信息)
            --top;//将当前节点弹出
        }
    }
    

    例题

    例题1:【洛谷2403】[SDOI2010] 所驼门王的宝藏

    例题2:【51nod1815】调查任务

  • 相关阅读:
    [读书笔记]黑客与画家[Hackers.and.Painters]
    android电池充电以及电量检测驱动分析
    LV在系统重启后不能自动激活(boot.lvm&after.loca)
    线段树菜鸟一题+归并排序【求逆序数】POJ2299
    【PAT】1035. Password (20)
    Android应用开发学习笔记之ContentProvider
    UVAlive 2322 Wooden Sticks(贪心)
    卸载QTP
    线段树模板
    2013 CSU校队选拔赛(1) 部分题解
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/SCC.html
Copyright © 2020-2023  润新知