洛谷UVA558 Wormholes

Description

给定一个有向图 \(G\) ,求 \(G\) 中是否存在负环。

Solution

既然这道题让我们要判断负环,那么我们肯定不能用 Dijkstra 了。但是,我们可以使用 Floyd 或 Bellman-Ford 算法来处理负权边。

Floyd 固然好写,但是数据范围限制了它:\(1 \leqslant n \leqslant 10^3\) 。所以,我选用了时间复杂度较优的 Bellman-Ford 算法 (因为它没死)

Bellman-Ford 算法的主体部分我就不详细说了,主要来说说如何判断负环。

根据定义,Bellman-Ford 算法会在至多 \(n - 1\) 次后完成所有的松弛操作,即执行完 Bellman-Ford 的主体部分后得出的结果数组就应当是最短路。

所以,我们可以在执行完 Bellman-Ford 算法后再去尝试进行松弛操作。此时,如果存在负环,那么一定可以继续松弛。反之,如果所有边都无法继续松弛,那么这个图不存在负环。

Code

#include <iostream>
#include <memory.h>
using namespace std;
const int MAXN = 1e3 + 10, MAXM = 2 * 1e3 + 10;

struct edge {
    int u, v, w;
};

edge e[MAXM];
int dis[MAXN];
int n, m, x, y, t;

void bellman_ford() {  // Bellman-Ford 主体部分
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (dis[e[j].v] >= dis[e[j].u] + e[j].w) {  // 松弛操作
                dis[e[j].v] = dis[e[j].u] + e[j].w;
            }
        }
    }
}

bool check_negative_cycle() {  // 检查是否存在负环
    for (int i = 1; i <= m; i++) {  // 循环每个边
        if (dis[e[i].v] > dis[e[i].u] + e[i].w) return 1;  // 如果仍然能够松弛,则代表有负权环存在
    }
    return 0;
}

void initialize() {  // 初始化
    memset(dis, 0x3f, sizeof(dis));
    dis[0] = 0;  // 从太阳系(点0)出发
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> t;
    while (t--) {
        cin >> n >> m;
        for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v >> e[i].w;
        bellman_ford();  // 先跑一遍Bellman-Ford
        if (check_negative_cycle()) cout << "possible" << endl;  // 然后检查有没有负权环
        else cout << "not possible" << endl;
    }
    return 0;
}
上一篇:[LOJ3280] JOISC2020 首都城市


下一篇:算法专题 | 10行代码实现的最短路算法——Bellman-ford与SPFA