コンテンツにスキップ

Bellman Ford (負閉路検出)

Reference

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#pragma once

template <typename T> struct BellmanFord {
  private:
    static constexpr T INF = numeric_limits<T>::max() / 2 - 1;

    struct Edge {
        int to;
        T cost;
    };

    int n;
    vector<vector<Edge>> G;
    vector<T> dist;

  public:
    explicit BellmanFord(int n) : n(n), G(n), dist(n, INF) {}

    void add_edge(int from, int to, T cost) { G[from].emplace_back(to, cost); }
    template <class Iter> void build(Iter first, Iter last) {
        dist.assign(n, INF);
        for (auto it = first; it != last; ++it) {
            dist[*it] = 0;
        }
        relax_edges();
        spread_neg_cycles();
    }
    void build(int s) { build(&s, &s + 1); }

    [[nodiscard]] bool reachable(int v) const { return dist[v] != INF; }
    [[nodiscard]] bool on_negative_cycle(int v) const { return dist[v] == -INF; }
    [[nodiscard]] bool valid(int v) const { return reachable(v) and !on_negative_cycle(v); }
    [[nodiscard]] T distance(int v) const { return dist[v]; }

  private:
    // ベルマンフォード
    void relax_edges() {
        for (int i = 0; i < n; ++i) {
            bool updated = false;
            for (int u = 0; u < n; ++u) {
                if (dist[u] == INF) continue;
                for (auto [v, w] : G[u]) {
                    if (dist[v] > dist[u] + w) {
                        dist[v] = dist[u] + w;
                        if (i == n - 1 || dist[v] < -INF) dist[v] = -INF;
                        updated = true;
                    }
                }
            }
            if (!updated) break;
        }
    }
    // 負閉路をBFSで伝播させる
    void spread_neg_cycles() {
        queue<int> q;
        vector<bool> used(n, false);
        for (int i = 0; i < n; ++i) {
            if (dist[i] == -INF) {
                used[i] = true;
                q.push(i);
            }
        }
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (auto [v, w] : G[u]) {
                if (!used[v]) {
                    used[v] = true;
                    dist[v] = -INF;
                    q.push(v);
                }
            }
        }
    }
};