算法笔记 - Kosaraju
概述
一个有向图求强联通分量的 \(Tarjan\) 平替。
通过两遍 \(dfs\) 来求解。
过程
先 \(Dfs\) 原图,在回溯时将节点入栈,扫描栈中的节点,如果未搜过就在反图开搜,将每次搜到的点归为一个 \(SCC\) 。
void Add(int u, int v) { e[u].push_back(v), g[v].push_back(u); }
void Dfs1(int u) { vis[u] = true; for (int v : e[u]) if (!vis[v]) Dfs1(v); d[++*d] = u; }
void Dfs2(int u) { bel[u] = scc; for (int v : g[u]) if (!bel[v]) Dfs2(v); }
int main() {
lep(i, 1, 2 * n) if (!vis[i]) Dfs1(i);
rep(i, 2 * n, 1) if (!bel[d[i]]) ++scc, Dfs2(d[i]);
}
证明
简单说明一下。
容易发现,我们在倒序扫描 \(Dfs\) 树的后缀遍历, \(SCC\) 的根会第一次被遍历到,而一个 \(SCC\) 在反向图上依旧是 \(SCC\) ,内部的所有点一定可以被正确加入。
那么我们可能会误将别的 \(SCC\) 中的点加入吗?
发现是不会的,因为在反图上,原本指出该 \(SCC\) 的边全部无法通行。
而原本指向该 \(SCC\) 的点,其一定会先于本 \(SCC\) 被染色。
算法正确性得证。
同时发现,\(SCC\) 的编号顺序即拓扑序。
例题
在 \(2-SAT\) 题目中使用最佳。
还有一些特殊的题目应用(待补)。
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下