【知识点】求桥算法(原创)

问题引入

洛谷模板题 P1656

给定一个无向连通图,已知没有重边、没有自环。求图中的所有


桥是什么?

桥(也称为割边)是指在无向图中,删除某条边后,图的连通分量数量增加的边。


这道题可以用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 $ ),存在简单环:

\[C = P_T(v, u) \cup \{ e \} \]

其中 $ 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. 由定理1,$ e $ 是树边;
  2. 由定理2,$ v $ 的子树无回边至 $ u $ 的祖先;
  3. 故处理回边时不会丢弃 $ e $ ;
  4. 回溯至 $ u $ 时 $ e $ 仍在栈顶,必被收集;

排他性证明

若 $ e = (u, v) $ 被收集:

  1. 每次收集的都是树边,所以 $ e $ 为树边,不妨设 $ u $ 为 $ v $ 的父亲;
  2. 由定理2的逆反定理,存在回边连接 $ v $ 的后代与 $ u $ 的祖先,则形成环,被处理掉,矛盾;
  3. 故 $ e' $ 是桥。

扩展算法

处理自环

如果有自环,读入的时候拒绝存储即可,反正自环也不可能是桥。(这个也不用证了吧?)

处理非连通图

如果图是非连通的,在\(main()\)函数中不能只以\(1\)作DFS起点,而是要枚举所有未处理的点作起点,时间复杂度是不变的。

一些感受

这个算法是我在不知道Tarjan算法的情况下,自己对着这个模板题想出来的。虽然没花多长时间,但是想出来的过程也没有一帆风顺,写出来缝缝补补,发现过了,很开心。这个算法相比Tarjan算法,扩展性就弱很多,因为Tarjan算法还可以用来解决很多问题。

posted @ 2025-08-19 21:47  Alkaid16  阅读(23)  评论(0)    收藏  举报