当前位置:首页 > 洛谷题解 > 洛谷P1656:用Tarjan算法找出关键铁路的奥秘

洛谷P1656:用Tarjan算法找出关键铁路的奥秘

1周前 (06-26)洛谷题解66

截图未命名.jpg 洛谷P1656:用Tarjan算法找出关键铁路的奥秘 Tarjan算法 割边/桥 图连通性 DFS遍历 洛谷P1656 第1张

一、算法核心思路

本题要求找出所有被删除后会破坏图连通性的边(即割边/桥)。我们采用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. 算法执行流程

  1. 初始化阶段:清空图数据,初始化时间戳

  2. DFS遍历:对每个未访问节点调用tarjan函数

  3. 割边判定:当子节点v无法绕过当前边(u,v)回到更早节点时

  4. 结果处理:对割边进行排序输出

3. 易错点提醒

  • 需要处理重边情况(本题数据保证无重边)

  • 输出要求按字典序排序

  • 节点编号从1开始

四、复杂度分析

  • 时间复杂度:O(V+E) 标准的DFS复杂度

  • 空间复杂度:O(V) 主要消耗在存储dfn/low数组



原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。