【知识点】求桥算法(原创)
问题引入
给定一个无向连通图,已知没有重边、没有自环。求图中的所有桥。
桥是什么?
桥(也称为割边)是指在无向图中,删除某条边后,图的连通分量数量增加的边。
这道题可以用Tarjan算法求强连通分量来完成,也可以用如下算法解决:
求桥算法
DFS的过程中,将所有经过的边存在一个栈中。则栈中存了一条路径。当遇到一个路径中出现过的点,则找到了一个环。将这个环弹出栈顶。如果没有找到此环,而该点的某条边延伸出的所有情况遍历结束,这条边还没有被弹出,就说明它不在任何一个强连通分量中,而是连接两个强连通分量的桥。我们弹出它,记录它作为答案。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
i64 n, m, u, v;
i64 vis[500];
vector<i64>edge[200];
stack<pair<i64, i64>>st;
priority_queue<pair<i64, i64>, vector<pair<i64, i64>>, greater<>>ans;
void ed() {
auto it = st.top();
i64 a = it.first, b = it.second;
if (a > b)
swap(a, b);
st.pop();
ans.push({a, b});
return;
}
void dfs(i64 now, i64 fa) {
for (auto v : edge[now]) {
if (v == fa)
continue;
if (vis[v] == 1) {
while (!st.empty() && st.top().first != v)
st.pop();
if (!st.empty() && st.top().first == v)
st.pop();
} else if (vis[v] == 0) {
st.push({now, v});
vis[v] = 1;
dfs(v, now);
}
if (!st.empty() && st.top().first == now)
ed();
}
vis[now] = 2;
return;
}
int main() {
cin >> n >> m;
for (i64 i = 0; i < m; i++) {
cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
vis[1] = 1;
dfs(1, 0);
while (!ans.empty()) {
auto it = ans.top();
ans.pop();
cout << it.first << ' ' << it.second << '\n';
}
return 0;
}
时间复杂度为\(O(n+m)\),与Tarjan算法一样。
不太严谨算法正确性证明
定义
- 图结构:设 $$ G = (V, E) $$ 为无向连通图,无重边无自环
- DFS 树:以顶点 1 为根进行 DFS 遍历生成的树 $ T $
- 树边:DFS 遍历使用的边 $ (u, v) \in T $
- 回边:连接后代与祖先的非树边
- 顶点状态:
- $ \text{vis}[v] = 0 $:未访问
- $ \text{vis}[v] = 1 $:访问中(在 DFS 栈中)
- $ \text{vis}[v] = 2 $:访问完成
证明
如何判断环
对任意回边 $ e = (u, v) $ (满足 $ \text{vis}[v] = 1 $ ),存在简单环:
其中 $ P_T(v, u) $ 是 $ T $ 中 $ v $ 到 $ u $ 的树路径。
如何判断桥
回边的出现会形成环,所以回边一定不是桥。(显而易见,环上的任何一条边都不是桥,就不证了嘻嘻),所以只需要看树边。(记为定理1)
树边 $ e = (u, v) $ ( $ u $ 是 $ v $ 的父节点)是桥,当且仅当不存在回边连接 $ v $ 的后代与 $ u $ 的祖先。(记为定理2)
证明:
- 必要性:若存在回边 $ (w, x) $ 连接 $ v $ 的后代 $ w $ 和 $ u $ 的祖先 $ x $,则 $ e \in $$ P_T(x, w) \cup {(w, x)} $,后者为环,所以 $ e $ 不是桥。
- 充分性:若无此回边,删除 $ e $ 后 $ u $ 与 $ v $ 不连通(因无环外路径)。
桥收集完备性
完备性证明
设 $ e = (u, v) $ 是任意桥:
- 由定理1,$ e $ 是树边;
- 由定理2,$ v $ 的子树无回边至 $ u $ 的祖先;
- 故处理回边时不会丢弃 $ e $ ;
- 回溯至 $ u $ 时 $ e $ 仍在栈顶,必被收集;
排他性证明
若 $ e = (u, v) $ 被收集:
- 每次收集的都是树边,所以 $ e $ 为树边,不妨设 $ u $ 为 $ v $ 的父亲;
- 由定理2的逆反定理,存在回边连接 $ v $ 的后代与 $ u $ 的祖先,则形成环,被处理掉,矛盾;
- 故 $ e' $ 是桥。
扩展算法
处理自环
如果有自环,读入的时候拒绝存储即可,反正自环也不可能是桥。(这个也不用证了吧?)
处理非连通图
如果图是非连通的,在\(main()\)函数中不能只以\(1\)作DFS起点,而是要枚举所有未处理的点作起点,时间复杂度是不变的。
一些感受
这个算法是我在不知道Tarjan算法的情况下,自己对着这个模板题想出来的。虽然没花多长时间,但是想出来的过程也没有一帆风顺,写出来缝缝补补,发现过了,很开心。这个算法相比Tarjan算法,扩展性就弱很多,因为Tarjan算法还可以用来解决很多问题。

浙公网安备 33010602011771号