\(Tarjan\)——强连通分量
首先了解几个概念:强连通,强连通图,强连通分量
- 强连通:在一个有向图\(G\)中,两个点\(a,b\),\(a\)可以走到\(b\),\(b\)可以走到\(a\),我们就说\((a,b)\)强连通
- 强连通图:在一个有向图\(G\)中,任意两个点都是强连通
- 强连通分量:在一个有向图\(G\)中,有一个子图,它任意两个点都是强连通,我们就说这个子图为强连通分量,特别的,一个点也是一个强连通分量
如图所示:
显然可得:\(1,2,3,5\) 构成了一个强连通分量(一个点也是)
首先引进一个概念:
- 时间戳,用数组\(dfn\)表示,也就是搜索这个图的顺序,每个节点的时间戳不同。
- \(low[x]\),以\(x\)为根的子树中,每个节点中连接的点的时间戳的最小值
\(low\)的初值:\(low[x]=dfn[x]\)
可能有点难懂,但这个非常重要,是核心思想,等一下的模拟过程会详细讲述 - 如何储存强连通分量呢,可以用 栈
算法步骤
- 每次遍历到一个新节点,就把它放进栈,如果这个点有出度,就继续往下找,直到不能再找
- 每一次回来都要更新\(low\)值,当然是取小的那个,如果发现\(low[x]=dfn[x]\)那么它的子节点中肯定有一个连上来,既然可以过去又可以回来,很明显是一个强连通分量。那么这个\(x\)就是这个强连通分量的根节点,那么栈中间,比这个\(x\)晚进来的点就是\(x\)的子节点,那么这些点全部出栈,就组成了一个强连通分量
到这来就完了,但是好像还是 没有理解透彻(反正我是这样)
那么就模拟一下,还是这张图\(5->4\)
- \(low[1]=dfn[1]=1\),\(1\)入栈
- \(low[2]=dfn[2]=2\),\(2\)入栈
- \(low[3]=dfn[3]=3\),\(3\)入栈
- \(low[5]=dfn[5]=4\),\(5\)入栈
然后发现\(5\)连接着\(1\),已经\(1\)寻找过的了,那么就看看,\(PK\)下谁才是真正的祖先
\(low[1]=1\),\(low[5]=4\),好吧,\(5\)输了,所以\(1\)是\(5\)的根节点,
\[\large low[5]=min(low[5],low[1])=1 \]
继续发现还有\(4\),\(low[4]=dfn[4]=5\), \(4\)入栈
但是\(4\)已经没有了出度,往回退
发现\(low[4]=dfn[4]\)那么\(4\)就是一个强连通分量的根节点(其实也就它一个),\(4\)退栈
继续往回退:\(low[5]=min(low[5],low[4])=1\);
继续:一直到\(1\),
\[\large low[3]=min(low[3],low[5])=1 \\ low[2]=min(low[2],low[3])=1 \\ low[1]=min(low[1],low[2])=1\]
发现此时\(low[1]=dfn[1]\),所以\(1\)也是 一个强连通分量的根,此时发现栈里还有\(1,2,3,5\),所以这个强连通分量为\(1,2,3,5\)
\(1\)还有一个出度:\(4\)
寻找\(4\),\(low[4]=dfn[4]=5\),发现没有出度
\(low[4]=dfn[4]\),所以\(4\)是一个强连通分量的根节点(还是只有他一个),退栈
往回退,\(low[1]=min(low[1],dfn[4])=1\);
这样就完了吗?
万一还有图没有遍历到呢
所以要加一个语句:
练习题
P2863 [USACO06JAN]The Cow Prom S题目描述
有一个 \(n\) 个点,\(m\) 条边的有向图,请求出这个图点数大于 \(1\)
输入格式
第一行为两个整数 \(n\) 和 \(m\)。
第二行至 \(m+1\) 行,每一行有两个整数 \(a\) 和 \(b\),表示有一条从 \(a\) 到 \(b\)
输出格式
仅一行,表示点数大于 \(1\)