From 2aa52ad35c25056bb43f22f1a016a46f45f18361 Mon Sep 17 00:00:00 2001 From: ei1333 Date: Tue, 11 Jun 2024 23:19:03 +0900 Subject: [PATCH 1/2] fix bellman ford --- docs/bellman-ford.md | 23 ++++++++++------ docs/longest-common-substring.md | 2 +- docs/static-rectangle-add-rectangle-sum.md | 2 +- graph/shortest-path/bellman-ford.hpp | 31 +++++++++++++--------- test/verify/aoj-grl-1-b.test.cpp | 7 ++++- 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/docs/bellman-ford.md b/docs/bellman-ford.md index 5043fc653..eeda68301 100644 --- a/docs/bellman-ford.md +++ b/docs/bellman-ford.md @@ -1,18 +1,25 @@ --- -documentation_of: //graph/shortest-path/bellman-ford.hpp +title: Bellman-Ford (単一始点最短路) +documentation_of: //graph/shortest-path/dijkstra.hpp --- -## 概要 +単一始点全点間最短路を求めるアルゴリズムです。負辺があっても動作します。経路上に負閉路がある場合はそれを検出します。 -単一始点全点間最短路を求めるアルゴリズム. 負辺があっても動作する. また負閉路も検出する. +# bellman_ford -負閉路がない場合, 全ての頂点への最短路に含まれる辺の本数は $V - 1$ 本以下である. したがって $V - 1$ 回すべての辺を走査して最短路を更新すると, 最終的な最短路が求まる. +```cpp +template +vector bellman_ford(const Edges &edges, int n, int s) +``` -負閉路がある場合 $V$ 回目に更新があるときだが, 始点とある頂点との $2$ 点間のパスに負閉路が含まれることと同値ではないので注意すること(これを判定した場合は負閉路からその頂点に到達可能か判定するか, 始点とある頂点から到達できない頂点を予め削除しておく必要がある, 実装しなさーい!). +頂点数 $n$ 、辺集合が `edges` からなる有向グラフについて、始点 $s$ から各頂点への最短路の重みを求め、それを返します。 -## 使い方 -* `bellman_ford(g, V, s)`: `V` 頂点の重み付きグラフ `g` で, 頂点 `s` から全点間の最短コストを求める. +ただし、始点 $s$ からその頂点に到達できない場合は `T` の最大値、その頂点までの経路上に負閉路が存在する場合は `T` の最小値が格納されます。 + +## 制約 + +- $0 \leq s \lt n$ ## 計算量 -* $O(VE)$ +* $O(V E)$ diff --git a/docs/longest-common-substring.md b/docs/longest-common-substring.md index 8c9326828..6c702a267 100644 --- a/docs/longest-common-substring.md +++ b/docs/longest-common-substring.md @@ -18,7 +18,7 @@ $s$ と $t$ の最長共通部分列を $S[a, b)$、$T[c, d)$ とします。こ ## 計算量 -$n = \max(|s|, |t|)$ とします。 +$n = \max(\|s\|, \|t\|)$ とします。 - `compress = false`: $O(n)$ - `compress = true`: $O(n \log n)$ diff --git a/docs/static-rectangle-add-rectangle-sum.md b/docs/static-rectangle-add-rectangle-sum.md index a6f023bfe..421398a66 100644 --- a/docs/static-rectangle-add-rectangle-sum.md +++ b/docs/static-rectangle-add-rectangle-sum.md @@ -20,7 +20,7 @@ documentation_of: //other/static-rectangle-add-rectangle-sum.hpp (2) で長方形の個数 $n$、クエリの個数 $q$ を指定した場合、領域を `reserve` するので少しだけ効率的です。 -# add_point +# add_rectangle ```cpp void add_rectangle(T l, T d, T r, T u, C w) diff --git a/graph/shortest-path/bellman-ford.hpp b/graph/shortest-path/bellman-ford.hpp index 850f5878c..ebae535cc 100644 --- a/graph/shortest-path/bellman-ford.hpp +++ b/graph/shortest-path/bellman-ford.hpp @@ -1,25 +1,32 @@ -#pragma once - #include "../graph-template.hpp" -/** - * @brief Bellman-Ford(単一始点最短路) - * - */ template -vector bellman_ford(const Edges &edges, int V, int s) { +vector bellman_ford(const Edges &edges, int n, int s) { const auto INF = numeric_limits::max(); - vector dist(V, INF); + const auto M_INF = numeric_limits::min(); + vector dist(n, INF); dist[s] = 0; - for (int i = 0; i < V - 1; i++) { + for (int i = 0; i < n - 1; i++) { for (auto &e : edges) { if (dist[e.from] == INF) continue; dist[e.to] = min(dist[e.to], dist[e.from] + e.cost); } } - for (auto &e : edges) { - if (dist[e.from] == INF) continue; - if (dist[e.from] + e.cost < dist[e.to]) return vector(); + vectornegative(n); + for(int i = 0; i < n; i++) { + for (auto &e: edges) { + if (dist[e.from] == INF) continue; + if (dist[e.from] + e.cost < dist[e.to]) { + dist[e.to] = dist[e.from] + e.cost; + negative[e.to] = true; + } + if(negative[e.from]) { + negative[e.to] = true; + } + } + } + for(int i = 0; i < n; i++) { + if(negative[i]) dist[i] = M_INF; } return dist; } diff --git a/test/verify/aoj-grl-1-b.test.cpp b/test/verify/aoj-grl-1-b.test.cpp index 22acee92c..4fa453523 100644 --- a/test/verify/aoj-grl-1-b.test.cpp +++ b/test/verify/aoj-grl-1-b.test.cpp @@ -14,7 +14,12 @@ int main() { es.emplace_back(a, b, c); } auto dists = bellman_ford(es, V, R); - if(dists.empty()) cout << "NEGATIVE CYCLE\n"; + for(auto& dist : dists) { + if(dist == numeric_limits< int >::min()) { + cout << "NEGATIVE CYCLE\n"; + return 0; + } + } for(auto &dist : dists) { if(dist == numeric_limits< int >::max()) cout << "INF\n"; else cout << dist << "\n"; From 13e96a03c034e0c304c91258dfc9c1c5d00697e4 Mon Sep 17 00:00:00 2001 From: ei1333 Date: Tue, 11 Jun 2024 23:20:33 +0900 Subject: [PATCH 2/2] apply clang-format --- docs/bellman-ford.md | 2 +- graph/shortest-path/bellman-ford.hpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/bellman-ford.md b/docs/bellman-ford.md index eeda68301..85e0743c9 100644 --- a/docs/bellman-ford.md +++ b/docs/bellman-ford.md @@ -1,6 +1,6 @@ --- title: Bellman-Ford (単一始点最短路) -documentation_of: //graph/shortest-path/dijkstra.hpp +documentation_of: //graph/shortest-path/bellman-ford.hpp --- 単一始点全点間最短路を求めるアルゴリズムです。負辺があっても動作します。経路上に負閉路がある場合はそれを検出します。 diff --git a/graph/shortest-path/bellman-ford.hpp b/graph/shortest-path/bellman-ford.hpp index ebae535cc..c824afd59 100644 --- a/graph/shortest-path/bellman-ford.hpp +++ b/graph/shortest-path/bellman-ford.hpp @@ -12,21 +12,21 @@ vector bellman_ford(const Edges &edges, int n, int s) { dist[e.to] = min(dist[e.to], dist[e.from] + e.cost); } } - vectornegative(n); - for(int i = 0; i < n; i++) { - for (auto &e: edges) { + vector negative(n); + for (int i = 0; i < n; i++) { + for (auto &e : edges) { if (dist[e.from] == INF) continue; if (dist[e.from] + e.cost < dist[e.to]) { dist[e.to] = dist[e.from] + e.cost; negative[e.to] = true; } - if(negative[e.from]) { + if (negative[e.from]) { negative[e.to] = true; } } } - for(int i = 0; i < n; i++) { - if(negative[i]) dist[i] = M_INF; + for (int i = 0; i < n; i++) { + if (negative[i]) dist[i] = M_INF; } return dist; }