Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bellmanfordの負閉路検出 #122

Merged
merged 2 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions docs/bellman-ford.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
---
title: Bellman-Ford (単一始点最短路)
documentation_of: //graph/shortest-path/bellman-ford.hpp
---

## 概要
単一始点全点間最短路を求めるアルゴリズムです。負辺があっても動作します。経路上に負閉路がある場合はそれを検出します。

単一始点全点間最短路を求めるアルゴリズム. 負辺があっても動作する. また負閉路も検出する.
# bellman_ford

負閉路がない場合, 全ての頂点への最短路に含まれる辺の本数は $V - 1$ 本以下である. したがって $V - 1$ 回すべての辺を走査して最短路を更新すると, 最終的な最短路が求まる.
```cpp
template <typename T>
vector<T> bellman_ford(const Edges<T> &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)$
2 changes: 1 addition & 1 deletion docs/longest-common-substring.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)$
2 changes: 1 addition & 1 deletion docs/static-rectangle-add-rectangle-sum.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
31 changes: 19 additions & 12 deletions graph/shortest-path/bellman-ford.hpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
#pragma once

#include "../graph-template.hpp"

/**
* @brief Bellman-Ford(単一始点最短路)
*
*/
template <typename T>
vector<T> bellman_ford(const Edges<T> &edges, int V, int s) {
vector<T> bellman_ford(const Edges<T> &edges, int n, int s) {
const auto INF = numeric_limits<T>::max();
vector<T> dist(V, INF);
const auto M_INF = numeric_limits<T>::min();
vector<T> 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<T>();
vector<bool> 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]) {
negative[e.to] = true;
}
}
}
for (int i = 0; i < n; i++) {
if (negative[i]) dist[i] = M_INF;
}
return dist;
}
7 changes: 6 additions & 1 deletion test/verify/aoj-grl-1-b.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Loading