Tarjan模板 人工栈版Tarjan

引入

tarjan算法有两个最重要东西
dfn[x]表示x的dfs序
low[x]表示x以及x能连到的点中(包括间接连到的)最小的dfn
还有两个标记
一个表示是否访问过
一个为是否在当前的栈中

求解

low何时能更新呢
设当前点为v,子节点为u
1、u没有访问过
那么先递归进去然后low[v]=min(low[u])
2、u访问过但在当前的栈中
这说明u为v的祖先,那么只能low[v]=min(dfn[u])

另外

除了强联通分量以外,tarjan还可以求桥边,割点等
求割点:任意一个 low[u]>=dfn[v]

Code

这个代码是单向图中,所以判断简单一点
如果在双向图中,需要复杂一点的判断

void tarjan(int x)
{
    low[x]=dfn[x]=++tot;p[++p[0]]=x;bz[x]=bz2[x]=1;
    for(int i=last[x];i;i=next[i])
    {
        int y=to[i];
        if(!bz[y])
        {
            tarjan(y);low[x]=min(low[x],low[y]);
        }
        else if(bz2[y]) low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x])
    {
        totot++;
        for(;p[p[0]+1]!=x;p[0]--)
        {
            int k=p[p[0]];c[k]=totot;bz2[k]=0;
        }
    }
}

然而有些题目的图是一条链的,会爆栈

人工栈版Tarjan

为了方便(复制)起见,这里调用tarjan(x)和上面递归的调用tarjan(x),效果是完全一样的
c是染色数组

void tarjan1()
{
    int x=zx[o];low[x]=dfn[x]=++tot;p[++p[0]]=x;bz[x]=bz2[x]=1;
    for(int i=last[x];i;i=next[i])
    {
        if(!bz[to[i]])
        {
            zk[o]=1;zi[o]=i;zx[++o]=to[i];zk[o]=0;return;
        } 
        else if(bz2[to[i]]) low[x]=min(low[x],dfn[to[i]]);
    }
    if(low[x]==dfn[x])
    {
        totot++;
        for(;p[p[0]+1]!=x;p[0]--) c[p[p[0]]]=totot,bz2[p[p[0]]]=0;
    }
    o--;
}
void tarjan2()
{
    int x=zx[o];low[x]=min(low[x],low[to[zi[o]]]);
    for(int i=next[zi[o]];i;i=next[i])
    {
        if(!bz[to[i]])
        {
            zk[o]=1;zi[o]=i;zx[++o]=to[i];zk[o]=0;return;
        } 
        else if(bz2[to[i]]) low[x]=min(low[x],dfn[to[i]]);
    }
    if(low[x]==dfn[x])
    {
        totot++;
        for(;p[p[0]+1]!=x;p[0]--) c[p[p[0]]]=totot,bz2[p[p[0]]]=0;
    }
    o--;
}
void tarjan(int x)
{
    zx[++o]=x;zk[o]=0;zi[o]=0;
    while(o>0){if(zk[o]==0) tarjan1(); else tarjan2();}
}

例题

【NOIP2016提高A组8.12】通讯
【NOIP2016提高A组8.11】种树
【NOIP2016提高A组模拟9.9】爬山
【NOIP2017提高A组冲刺11.5】轰炸

你可能感兴趣的:(模板库,Tarjan,模板库)