洛谷P1656:用Tarjan算法找出关键铁路的奥秘
一、算法核心思路
本题要求找出所有被删除后会破坏图连通性的边(即割边/桥)。我们采用Tarjan算法实现,其核心原理是通过DFS遍历记录每个节点的访问次序(dfn)和能回溯到的最早节点(low),当满足low[v] > dfn[u]时,边(u,v)即为割边。
二、完整AC代码(带详细注释)
#include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 155; vector<int> G[MAXN]; // 邻接表存图 int dfn[MAXN], low[MAXN]; int timestamp = 0; // 时间戳 vector<pair<int, int>> bridges; // 存储所有割边 void tarjan(int u, int father) { dfn[u] = low[u] = ++timestamp; for (int v : G[u]) { if (!dfn[v]) { // 未访问过的节点 tarjan(v, u); low[u] = min(low[u], low[v]); // 发现割边条件 if (low[v] > dfn[u]) { bridges.push_back({min(u,v), max(u,v)}); } } else if (v != father) { // 处理回边 low[u] = min(low[u], dfn[v]); } } } int main() { int n, m; cin >> n >> m; // 建图 while (m--) { int a, b; cin >> a >> b; G[a].push_back(b); G[b].push_back(a); } // 执行Tarjan算法 for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i, -1); } // 输出结果(按字典序排序) sort(bridges.begin(), bridges.end()); for (auto &p : bridges) { cout << p.first << " " << p.second << endl; } return 0; }
三、代码解析
1. 关键变量说明
dfn数组
:记录每个节点的DFS序( discovery time)low数组
:记录通过回边能到达的最早祖先节点bridges容器
:动态存储找到的所有割边
2. 算法执行流程
初始化阶段:清空图数据,初始化时间戳
DFS遍历:对每个未访问节点调用tarjan函数
割边判定:当子节点v无法绕过当前边(u,v)回到更早节点时
结果处理:对割边进行排序输出
3. 易错点提醒
需要处理重边情况(本题数据保证无重边)
输出要求按字典序排序
节点编号从1开始
四、复杂度分析
时间复杂度:O(V+E) 标准的DFS复杂度
空间复杂度:O(V) 主要消耗在存储dfn/low数组
原创内容 转载请注明出处