tarjan缩点(洛谷P3387)

此题解部分借鉴于九野的博客html

题目分析

  • 给定一个 $n$ 个点 $m$ 条边有向图,每一个点有一个权值,求一条路径,使路径通过的点权值之和最大。你只须要求出这个权值和。数组

  • 容许屡次通过一条边或者一个点,可是,重复通过的点,权值只计算一次。url

  • 假如没有后面这条限制的话,那图必定是一个无环图。由于有环的话我能够一直在环上跑,因此答案就没有一个上界spa

  • 没有环的话我萌能够很天然地想到一个 $O(n)$ 的 拓扑$dp$ 作法,先作入度为 $0$ 的点,更新入度不为 $0$ 的点,把更新后入度为 $0$ 的点加入队列里,继续作以前的事情.net

  • 如今考虑有环怎么作code

  • 有一个贪心的思路是,到环上就先把环上的点都走完,再从环上任意一点出发htm

  • 其实这个环能够看作一个大点,也就是咱们今天要介绍的主角 $\to$ 缩点blog


tarjan缩点

下面这张图是从 九野的博客copy 过来的队列

  • 把能够互相抵达的点集叫作一个连通份量
  • 最大的那个能够互相抵达的点点集即为强连通份量
  • 特别的,单个的点也能够是强连通份量

好比说 :${ 4, 5 }$ 是一个联通份量,而 ${4,5,6 } $ 则是一个强连通份量(一个大点)get

tarjan的过程就是经过 dfs 找强连通份量(大点)的过程

对图dfs一下,遍历全部未遍历过的点 ,会获得一个有向树,显然有向树是没有环的。(注意搜过的点不会再搜

则能产生环的 <font color = red>只有(指向已经遍历过的点)的边</font>

咱们发现 $7 \to 3$ (<font color = red>红边</font> / 横叉边)这种边必定不会产生连通份量

而 $6 \to 4$ (<font color = green>绿边 </font> / 返祖边)这种边必定会产生联通份量

具体来讲:具备<font color = red>父子关系</font>的边必定会产生联通份量

咱们在dfs的时候须要用一个来保存当前所在路径上的点<font color = red>(栈中全部点必定是有父子关系的)</font>

咱们用一些数组来表示dfs的过程

int tim, dfn[MAX], low[MAX]

$dfn[i]$ 表示遍历到节点 $i$ 的时间戳(第几回遍历)

$low[i]$ 表示往上能够到达最先的点

初始化 $dfn[i] = low[i] = ++tim$

咱们能够根据上面过程的步骤写出如下代码

for(int i = head[u]; i; i = nex[i]) {
        if(dfn[to[i]]) {
            if(instack[to[i]]) {
            	if(low[to[i]] < low[u]) {
                	low[u] = low[to[i]];
                }
            }
        } else {
            dfs(to[i]);
            if(low[to[i]] < low[u]) {
            	low[u] = low[to[i]];
            }
        }
    }

假如当前节点 $u$, $dfn[u] == low[u]$ 那就说明栈顶元素一直到节点 $u$ 都属于一个强联通份量,感性理解

把栈中元素弹出而且把他们都给标记为同一种颜色(同一个强连通份量)

if(dfn[u] == low[u]) {
        ++totcol;
        do {
            int v = stk[top];
            col[v] = totcol;
            instack[v] = false;
        } while(stk[top--] != u);
    }

具体实现看代码

$\color {Deepskyblue} {Code}$

这里还有一道 tarjan练手好题

原文出处:https://www.cnblogs.com/Lskkkno1/p/11521301.html

相关文章
相关标签/搜索