强连通分量-Tarjan

作用

Tarjan算法可以将强连通分量合并到该强连通分量的某一个点上,通俗地说就是实现环缩点。

有向图的强连通分量

有向图中,如果两个点i,j之间既有i->j的路径,又有j->i的路径,则i,j强连通。所有点都强连通的图是强连通图,一个有向图中的所有最大(就是不能再扩大了)的强连通子图都是这个有向图的强连通分量。
有时候我们不需要考虑强连通分量中的点,而是需要直接考虑整个强连通分量。这就需要我们找出图中的强连通分量,Tarjan是不错的选择。

实现

我们记录时间戳ti,然后为每个点都记录dfn[i](i的最早访问时间)和low[i](i能追溯到的最早时间),再开一个栈stk表示目前处理的强连通分量中的点。访问一个点i时,先dfn[i]=low[i]=++ti,然后从i开始遍历,假设目前处理的i的儿子是son,那么分为两种情况:
1.son还没处理过,那么就递归调用Tarjan(son),先把son[j]处理出来,然后追溯到low[son],所以low[i]=min(low[i],low[son])。
2.son已经处理过了,还要分两种情况:
(1)son不在栈中,说明son是其他强连通分量中的点,故不处理。
(2)son在栈中,说明son是目前处理的强连通分量中的点,那么就可以被i追溯,所以low[i]=min(low[i],dfn[son])。
一个坑:这里到底是low[son]还是dfn[son]会感觉傻傻分不清,好像如果只求强连通分量效果是相同的,但由于Tarjan还可以求双连通分量,所以dfn[son]才是符合定义的,但是我并不会双连通分量啊QAQ!所以会了再来填坑吧。

模板

void Tarjan(int x)
{
    dfn[x]=low[x]=++ti;instk[x]=true;stk[++top]=x;
    for (int j=lnk[x];j;j=nxt[j]) //这里用邻接表存图
        if (!dfn[son[j]]) Tarjan(son[j]),low[x]=min(low[x],low[son[j]]); else
        if (instk[son[j]]) low[x]=min(low[x],dfn[son[j]]);
    int y;
    if (dfn[x]==low[x]) do //将栈中所有节点并入x
    {
        y=stk[top--];instk[y]=false;
        father[y]=x;num[x]++; //father表示父亲,num表示节点个数
    } while (x!=y);
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值