tarjan 缩点

tarjan啊(⊙o⊙)…
嗯。。我的理解可能比较诡异

先说缩点
洛谷P3387 的模板

题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

输出格式:
共一行,最大的点权之和。

最后的树形dp就先忽略,先只说缩点
需要缩点的时候我们确实一看就知道,但是像我这种啥模板也没写过的
就开始纠结怎么缩点了

先要理解核心的两个数组的本质(⊙o⊙)…
所谓dfn[ ]时间戳,就是记录一下你搜到这个点的时间
然后low[ ],应该可以说是你从这个点继续往下走,如果有环的话你是会回到之前你走的那个点的,那么low[ ]的值就是你可以回到的最早的那个点的dfn[ ]

然后是赋值
dfn[ ]好说,一搜进来就给一个dfn[ ]=++time;
low[ ]的话{ (to就是遍历边的那个连接的下一个的点)
if( !dfn[to] ) tarjan(to) , low[pos] = min( low[pos] , low[to] )
else if( vis[to] ) low[pos] = min( low[pos] , dfn[to] )
}(vis 是一个入栈的标记)
也就是说有分两种情况
第一种情况:如果你还没有对to进行过任何处理,那你就得先进去看看了2333
第二种情况:如果to已经被你搞过了,这个时候你的low可不能跟着他的low跑,那样你可能跟着他跑到别的环里面去。 如果它已经跟着别人组成一个环缩成一个点了,那它就不在栈内,那你肯定不能和它缩成一个点,只能自己新建一个团队了。所以只有(vis[to])的时候你才能跟着他赋值。

不难想出在我们这样的处理之后如果仍然有(low[pos]==dfn[pos])
那么我们从最后一个入栈的点一个一个往出取,直到们取到pos,那么这些取出来的点将来一定是同一个点,这个时候我们就可以用一些很基础的方法把它们记录在一起了
嗯》。。。这里先就只放核心代码吧:

void tarjan(int pos)
{
    dfn[pos]=low[pos]=++tim;
    stack[++top]=pos;vis[pos]=true;
    for(int i=head[pos],v=edge[i].to;i;i=edge[i].nex,v=edge[i].to)
        if(!dfn[v])      tarjan(v),low[pos]=min(low[pos],low[v]);
        else if(vis[v])  low[pos]=min(low[pos],dfn[v]);
    if(low[pos]==dfn[pos]){
        ++tot;vis[pos]=false;
        while(stack[top+1]!=pos){
            zz[stack[top]]=tot;
            vis[stack[top]=false;
            top--;
        }
    }
}
int main()
{
    for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
}

你可能感兴趣的:(c++图论)