把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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】调查任务

posted @ 2018-10-28 21:51  TheLostWeak  阅读(342)  评论(0编辑  收藏  举报