有向图的强连通分量

firstlight Lv2

定义:

给定一张有向图。若对于图中任意两个节点 ,既存在从 的路径,也存在从 的路径,则称该有向图是 “强连通图” 。有向图的极大强连通子图被称为 “强连通分量“,简记为

SCC

如图,红色部分即为一个

Tarjan算法:

实现原理:

1. 处理参数:

对于每一个点 ,其都有两个参数

定义:
  1. :遍历到点 时的时间戳
  2. :点 可到达的 ​ 值最小的点
求出参数:

对该图进行一遍 ​,使得该图中每个点都被遍历到且仅被遍历一次

在进行 时:

遍历每个点的同时都将其压入栈中

对于 : 我们为每一个点 赋予一个时间戳(即 时的次序),并将其记录在

的值有两种情况:

假设点 的子节点为

  1. 当点 已经被遍历过时: 则判断 是否在栈中(是否为 所在的 的一部分)若在则将 更新为 (为了防止 连向 值较小的点)
  2. 当点 未被遍历过时: 将 更新为

①横插边:对于图中的一条边 ,如果满足 不是 的祖先,则边 为一条横插边。横插边一定满足

2. 找出SCC:

判定:

设当前遍历到点 ,则当 ​ 时,在栈中的所有点即为一个强连通分量的所有点

证明:

时,点 无法在向 值更小的地方走,而点 是栈中的除 外的所有节点的子孙节点,且点 是可以走到点

这说明从点 出发一定可以经过栈中除 外的所有节点在回到 ,即栈中的所有结点构成了一个环

而环一定满足 的定义

所以判定成立

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int n, m;
int h[N], e[M], ne[M], idx; // 链式前向星存图
int dfn[N], low[N], timestamp; // 两个参数和时间戳记录
int id[N], scc_cnt; // SCC编号和计数
int stk[N], top; // 栈
bool in_stk[N]; // 记录是否在栈中

void tarjan(int u)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u, in_stk[u] = true;

for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if(in_stk[j]) low[u] = min(low[u], dfn[j]);
}

if(dfn[u] == low[u])
{
int y;
++ scc_cnt; // SCC的个数增加
do {
y = stk[top -- ]; // 将满足条件的数全部弹出, 加入到新的SCC中
in_stk[y] = false;
id[y] = scc_cnt;
siz[scc_cnt] ++ ;
} while(y != u); // 直到再次回到u为止(这说明环上的点已经全部弹出)
}
}

main()中要保证最后所有点的 都不为零

所以有代码:

1
2
for(int i = 1; i <= n; i ++ ) 
if(!dfn[i]) tarjan(i);

应用:缩点

定理:

由所有 组成的图一定是一张拓扑图(即有向无环图,简称

而且 编号的倒序一定是拓扑序

证明:

SCC2

如图,分为两种情况讨论:

  1. 先遍历子图 : 这种情况一定会在某个地方断掉(如边 ),之后的 是该子图缩点后的祖先节点,满足定理
  2. 先遍历子图 : 这种情况会一次遍历所有点,该子图缩点后为之后的 的祖先节点,满足定理

所以定理成立

  • 标题: 有向图的强连通分量
  • 作者: firstlight
  • 创建于 : 2024-03-13 18:55:36
  • 更新于 : 2025-01-22 22:41:04
  • 链接: https://blog.firstlightport.top/posts/scc/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论