「补档计划」Tarjan

复习 Tarjan 算法以及一些常见的应用。

一些定义

  • 树枝边:DFS 时经过的边,即 DFS 搜索树上的边
  • 前向边:与 DFS 方向一致,从某个结点指向其某个子孙的边
  • 后向边:与 DFS 方向相反,从某个结点指向其某个祖先的边
  • 横叉边:从某个结点指向搜索树中另一子树中的某结点的边

强连通分量

定义

在有向图 $G$ 中, 如果两个顶点 u, v 间存在一条路径 uv 的路径且也存在一条 vu 的路径,则称这两个顶点 u, v 是强连通的(strongly connected)。如果有向图 $G$ 的每两个顶点都强连通, 称 $G$ 是一个强连通图。 有向非强连通图的极大强连通子图, 称为强连通分量(strongly connected components)。若将有向图中的强连通分量都缩为一个点,则原图会形成一个 DAG(有向无环图)。

极大强连通子图: $G$ 是一个极大强连通子图当且仅当 $G$ 是一个强连通子图且不存在另一个强连通子图 $G’$ 使得 $G$ 是 $G’$ 的真子集。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
std::vector <int> edge[MAXN];

int size[MAXN], dfn[MAXN], low[MAXN], id[MAXN], idx, cnt;
bool vis[MAXN];

inline void tarjan(const int u) {
static std::stack<int> st;
register int v;
vis[u] = true, st.push(u), low[u] = dfn[u] = ++idx;
for (register int i = 0, v; i < edge[u].size(); i++) {
if (!dfn[v = edge[u][i]]) tarjan(v), low[u] = std::min(low[u], low[v]);
else if (vis[v]) low[u] = std::min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
cnt++;
do vis[v = st.top()] = false, st.pop(), size[id[v] = cnt]++;
while (u != v);
}
}

inline void tarjan() {
for (register int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
}

割点

定义

无向连通图中,如果删除某点后,图变成不连通,则称该点为割点。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
std::vector <int> edge[MAXN];

int idx, fa[MAXN], dfn[MAXN], low[MAXN], cut[MAXN];

inline void tarjan(const int u) {
dfn[u] = low[u] = ++idx;
for (register int i = 0, v; i < edge[u].size(); i++) {
if (!dfn[v = edge[u][i]])
fa[v] = u, tarjan(v), low[u] = std::min(low[u], low[v]);
else if (u != fa[u]) low[u] = std::min(low[u], dfn[v]);
}
}

inline void findCut(const int n) {
register int son = 0;
tarjan(1);
for (register int i = 2, v; i <= n; i++) {
if (low[i] >= dfn[v = fa[i]])
v == 1 ? son++ : cut[v]++;
}
if (son > 1) cut[1] = son - 1;
}

割边/桥

定义

存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥无向连通图中,如果删除某边后,图变成不连通,则称该边为桥。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
std::vector <int> edge[MAXN];

int dfn[MAXN], low[MAXN], idx, fa[MAXN];

inline void tarjan(const int u) {
dfn[u] = low[u] = ++idx;
for (register int i = 0, v; i < edge[u].size(); i++) {
if (!dfn[v = edge[u][i]])
fa[v] = u, tarjan(v), low[u] = std::min(low[u], low[v]);
else if (u != fa[u]) low[u] = std::min(low[u], dfn[v]);
}
}

inline std::vector<std::pair<int, int> > findBridge(const int n) {
tarjan(1);
std::vector<std::pair<int, int> > bridge;
for (register int i = 1, v; i <= n; i++)
if ((v = fa[i]) && low[i] > dfn[v])
bridge.push_back(std::make_pair(v, i));
return bridge;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×