前言
做 T a r j a n \tt Tarjan Tarjan 的题,屑教练给的课件实在是太La Ji了,于是重新整理一下 T a r j a n \tt Tarjan Tarjan 的思路
Tarjan
T a r j a n \tt Tarjan Tarjan算法,用于解决有向图的强联通分量问题。(当然不只是,这里是这样的)
在有向图中,若两点任意可达,则两点之间强联通。若有向图任意两点强联通,则称为强连通图。非强联通图的极大强联通子图称作强联通分量。 T a r j a n \tt Tarjan Tarjan 算法就是用于找有向图的强联通分量。
首先,有三个概念需要了解:
- 时间戳:即 d f s dfs dfs序: d f n dfn dfn。
- 搜索树:从某个节点出发访问,每个节点只被第一次访问,那么搜索到的节点由搜索边形成的一棵树,称作搜索树。
- 追溯值:记为 l o w [ x ] low[x] low[x],表示以 x x x 作为搜索树的树根出发,能访问到的 d f s dfs dfs 序最小的点。这里能访问到的并不只是搜索树上的节点,还包括能通过某条非搜索树边到达搜索树上的节点。
那么我们用一个栈来维护这棵搜索树。对于图上的环,当访问到某个在栈内的节点时,即形成了环。
当搜完一个没被访问过的节点,更新
l
o
w
[
x
]
:
l
o
w
[
x
]
=
m
i
n
(
l
o
w
[
x
]
,
l
o
w
[
t
o
]
)
low[x]:low[x]=min(low[x],low[to])
low[x]:low[x]=min(low[x],low[to])
当搜到一个在栈内的节点,有环了,那么这是将这个点作为强联通分量的根(抽象理解即可)。更新
l
o
w
[
x
]
=
m
i
n
(
l
o
w
[
x
]
,
d
f
n
[
t
o
]
)
low[x]=min(low[x],dfn[to])
low[x]=min(low[x],dfn[to])
当搜索完一个节点,且 d f n [ x ] = l o w [ x ] dfn[x]=low[x] dfn[x]=low[x],那么这个时候这里就形成了一个强联通分量。退栈,用 v e c t o r \tt vector vector 记录下这个强联通分量即可。
感性理解为: d f n dfn dfn 是一些向下的边, l o w [ x ] low[x] low[x]是一条返回环的边,这个时候形成了一个联通分量。若 d f n [ x ] = = l o w [ x ] dfn[x]==low[x] dfn[x]==low[x],则找到了一个最大环,也就是一个强联通分量。
Code:
void Tarjan(LL x)
{
S.push( x );
InStack[x] = 1;
Dfn[x] = Low[x] = ++ Cnt;
int l = G[x].size();
for (Int i = 0; i < l; ++ i)
{
LL to = G[x][i];
if (! Dfn[to])
{
Tarjan( to );
Low[x] = Min(Low[x], Low[to]);
}
else if ( InStack[to] )
Low[x] = Min(Low[x], Dfn[to]);
}
if (Dfn[x] == Low[x])
{
LL u;
do
{
u = S.top();
S.pop();
InStack[u] = 0;
} while(u != x);
}
}