競技プログラミング用 C++ ライブラリ
graph/max_flow.hpp · GitHub で見る
共通ページ
#include "lib/graph/max_flow.hpp"
MaxFlow<> graph(3);
graph.add_edge(0, 1, 5);
graph.add_edge(1, 2, 3);
auto flow = graph.flow(0, 2);
auto cut = graph.min_cut(0);
| 操作・型 | 内容・返り値 | 計算量 |
|---|---|---|
MaxFlow<Cap>(n=0) |
n 頂点で構築。Cap の既定は std::int64_t
|
\(O(N)\) |
size() / edge_count()
|
頂点数/追加した辺数 | \(O(1)\) |
reserve(m) |
元の辺 m 本分の領域を確保 |
\(O(M)\) |
add_edge(from,to,cap) |
有向辺を追加し、0 始まりの辺番号を返す | 償却 \(O(1)\) |
flow(s,t) |
最大流まで流し、今回追加した流量を返す。Highest Label Push-Relabel を使う | \(O(N^2M)\)、初回は別途 \(O(N+M)\) |
flow(s,t,limit) |
今回の流量を limit 以下に制限し、追加した流量を返す。反復版 Dinic を使う |
\(O(N^2M)\)、初回は別途 \(O(N+M)\) |
min_cut(s) |
残余グラフで s から到達できるかを vector<bool> で返す |
\(O(N+M)\) |
get_edge(i) |
追加時の順番で辺の情報を返す | \(O(1)\) |
edges() |
全ての辺の情報を vector<Edge> で返す |
\(O(M)\) |
change_edge(i,cap,flow) |
辺の容量と流量を変更。0 <= flow <= cap が必要 |
\(O(1)\) |
reset_flow() |
全ての流量を 0 に戻す。頂点と辺は残す | \(O(M)\) |
Edge |
from・to・cap・flow を持つ |
—(型) |
頂点は 0 始まりです。自己辺・多重辺・容量 0 に対応します。Cap は 64 bit 以下の標準整数型で、bool を除きます。容量・流量上限は非負、返す流量と各辺の容量は Cap に収めます。Push-Relabel の途中の余剰流量は 128 bit で持つため、始点から出る容量の総和が Cap を超える場合も扱えます。
s != t が必要です。頂点数は INT_MAX/2-1 以下、元の辺数は INT_MAX/2 以下にします。再帰を使わないため、長いパスも扱えます。
残余グラフを保持するため、同じ始点・終点で繰り返し呼ぶと続きを流します。辺を追加してから続けることもできます。change_edge で現在のフローを変更する場合は、始点・終点以外の流量保存則を保ってください。最小カットが必要な場合は、flow(s,t) で最大流まで流した後に min_cut(s) を呼びます。
\(N\) 頂点・\(M\) 辺として、構築は \(O(N)\) 以下、辺追加は償却 \(O(1)\)、最大流はどちらも一般の上界が \(O(N^2M)\)、min_cut は \(O(N+M)\) です。辺追加後の最初の流量計算・min_cut には隣接配列の構築 \(O(N+M)\) が加わります。get_edge・値取得・change_edge は \(O(1)\)、edges・reset_flow は \(O(M)\)、領域を増やす reserve は \(O(M)\)、空間は \(O(N+M)\) です。
#ifndef MAX_FLOW_HPP
#define MAX_FLOW_HPP
#include "../components/flow_graph.hpp"
#include "../components/push_relabel.hpp"
#include <cstdint>
#include <type_traits>
template<class Cap = std::int64_t> class MaxFlow {
static_assert(std::is_integral_v<Cap> && !std::is_same_v<Cap, bool> && sizeof(Cap) <= 8);
flow_detail::Graph<Cap> graph_;
void vertex([[maybe_unused]] int v) const { assert(0 <= v && v < graph_.n); }
int index(int i) const { assert(0 <= i && std::size_t(i) < graph_.arcs.size() / 2); return 2 * i; }
public:
struct Edge { int from, to; Cap cap, flow; };
explicit MaxFlow(int n = 0) : graph_(n) {}
int size() const { return graph_.n; }
int edge_count() const { return int(graph_.arcs.size() / 2); }
void reserve(std::size_t m) { graph_.reserve(m); }
int add_edge(int from, int to, Cap cap) { return graph_.add_edge(from, to, cap); }
Edge get_edge(int i) const {
int j = index(i);
const auto& a = graph_.arcs[j];
const auto& b = graph_.arcs[j ^ 1];
return {b.to, a.to, Cap(a.cap + b.cap), b.cap};
}
std::vector<Edge> edges() const {
std::vector<Edge> result;
result.reserve(edge_count());
for (int i = 0; i < edge_count(); ++i) result.push_back(get_edge(i));
return result;
}
void change_edge(int i, Cap cap, Cap flow) {
assert(0 <= flow && flow <= cap);
int j = index(i);
graph_.arcs[j].cap = cap - flow, graph_.arcs[j ^ 1].cap = flow;
}
void reset_flow() { graph_.reset_flow(); }
Cap flow(int s, int t) {
vertex(s), vertex(t);
assert(s != t);
return flow_detail::push_relabel(graph_, s, t);
}
Cap flow(int s, int t, Cap limit) {
vertex(s), vertex(t);
assert(s != t && limit >= 0);
graph_.build();
auto& g = graph_;
std::vector<int> level(g.n), iter(g.n), queue(g.n);
struct Frame { int v; Cap limit, sent; };
std::vector<Frame> stack;
stack.reserve(g.n);
Cap total = 0;
while (total < limit) {
std::fill(level.begin(), level.end(), -1);
level[s] = 0;
int head = 0, tail = 0;
queue[tail++] = s;
while (head < tail && level[t] < 0) {
int v = queue[head++];
for (int i = g.start[v]; i < g.start[v + 1]; ++i) {
const auto& e = g.arcs[g.order[i]];
if (!e.cap || level[e.to] >= 0) continue;
level[e.to] = level[v] + 1;
queue[tail++] = e.to;
}
}
if (level[t] < 0) break;
std::copy_n(g.start.begin(), g.n, iter.begin());
stack.push_back({t, Cap(limit - total), Cap(0)});
while (!stack.empty()) {
auto& frame = stack.back();
int v = frame.v;
if (v == s || frame.sent == frame.limit || iter[v] == g.start[v + 1]) {
Cap sent = v == s ? frame.limit : frame.sent;
if (v != s && sent < frame.limit) level[v] = g.n;
stack.pop_back();
if (stack.empty()) { total += sent; break; }
auto& parent = stack.back();
int id = g.order[iter[parent.v]];
g.arcs[id].cap += sent, g.arcs[id ^ 1].cap -= sent;
parent.sent += sent;
if (parent.sent < parent.limit) ++iter[parent.v];
} else {
int id = g.order[iter[v]];
const auto& e = g.arcs[id];
if (level[e.to] < 0 || level[e.to] >= level[v] || !g.arcs[id ^ 1].cap) ++iter[v];
else stack.push_back({e.to, std::min<Cap>(frame.limit - frame.sent, g.arcs[id ^ 1].cap), Cap(0)});
}
}
}
return total;
}
std::vector<bool> min_cut(int s) {
vertex(s);
graph_.build();
std::vector<bool> visited(graph_.n);
std::vector<int> queue{ s };
visited[s] = true;
for (std::size_t head = 0; head < queue.size(); ++head) {
int v = queue[head];
for (int i = graph_.start[v]; i < graph_.start[v + 1]; ++i) {
const auto& e = graph_.arcs[graph_.order[i]];
if (e.cap && !visited[e.to]) visited[e.to] = true, queue.push_back(e.to);
}
}
return visited;
}
};
#endif
#line 1 "graph/max_flow.hpp"
#line 1 "components/flow_graph.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <limits>
#include <vector>
namespace flow_detail {
template<class Cap> struct Graph {
struct Arc { int to; Cap cap; };
int n;
std::vector<Arc> arcs;
std::vector<int> start, order;
bool built = false;
explicit Graph(int n = 0) : n(n) { assert(n >= 0); }
void reserve(std::size_t m) { assert(m <= std::size_t(std::numeric_limits<int>::max() / 2)); arcs.reserve(2 * m); }
int add_edge(int from, int to, Cap cap) {
assert(0 <= from && from < n && 0 <= to && to < n && cap >= 0);
assert(arcs.size() <= std::size_t(std::numeric_limits<int>::max() - 2));
int id = int(arcs.size());
arcs.push_back({to, cap});
arcs.push_back({from, Cap(0)});
built = false;
return id / 2;
}
void build() {
if (built) return;
start.assign(std::size_t(n) + 1, 0);
for (std::size_t i = 0; i < arcs.size(); ++i) ++start[arcs[i ^ 1].to + 1];
for (int v = 0; v < n; ++v) start[v + 1] += start[v];
order.resize(arcs.size());
auto next = start;
for (int i = 0; i < int(arcs.size()); ++i) order[next[arcs[i ^ 1].to]++] = i;
built = true;
}
void reset_flow() {
for (std::size_t i = 0; i < arcs.size(); i += 2) arcs[i].cap += arcs[i + 1].cap, arcs[i + 1].cap = 0;
}
};
}
#line 1 "components/push_relabel.hpp"
#line 5 "components/push_relabel.hpp"
#include <cstdint>
namespace flow_detail {
template<class Cap> Cap push_relabel(Graph<Cap>& graph, int s, int t) {
graph.build();
int n = graph.n;
assert(n <= std::numeric_limits<int>::max() / 2 - 1);
int dead = 2 * n;
std::vector<__uint128_t> excess(n);
std::vector<int> height(n), current(n), count(std::size_t(dead) + 1), head(std::size_t(dead) + 1, -1), next(n), queue(n);
std::vector<unsigned char> active(n);
int highest = -1;
std::size_t work = 0, threshold = std::max<std::size_t>(1, 4 * graph.arcs.size() + n);
auto activate = [&](int v) {
if (v == s || v == t || active[v] || !excess[v] || height[v] == dead) return;
active[v] = 1;
next[v] = head[height[v]], head[height[v]] = v;
highest = std::max(highest, height[v]);
};
auto buckets = [&] {
std::fill(head.begin(), head.end(), -1);
std::fill(active.begin(), active.end(), 0);
highest = -1;
for (int v = 0; v < n; ++v) activate(v);
};
auto global = [&] {
std::fill(height.begin(), height.end(), n + 1);
std::fill(count.begin(), count.end(), 0);
std::copy_n(graph.start.begin(), n, current.begin());
height[s] = n, height[t] = 0;
int first = 0, last = 0;
queue[last++] = t;
while (first < last) {
int v = queue[first++];
for (int j = graph.start[v]; j < graph.start[v + 1]; ++j) {
int i = graph.order[j], u = graph.arcs[i].to;
if (u != s && height[u] == n + 1 && graph.arcs[i ^ 1].cap) height[u] = height[v] + 1, queue[last++] = u;
}
}
for (int h : height) ++count[h];
buckets();
work = 0;
};
for (int j = graph.start[s]; j < graph.start[s + 1]; ++j) {
int i = graph.order[j];
auto& e = graph.arcs[i];
if (e.to == s || !e.cap) continue;
excess[e.to] += e.cap, graph.arcs[i ^ 1].cap += e.cap, e.cap = 0;
}
global();
while (highest >= 0) {
if (head[highest] < 0) { --highest; continue; }
int v = head[highest];
head[highest] = next[v];
if (!active[v] || height[v] != highest) continue;
active[v] = 0;
while (excess[v] && height[v] < dead) {
if (current[v] == graph.start[v + 1]) {
int old = height[v], value = dead;
for (int j = graph.start[v]; j < graph.start[v + 1]; ++j) {
const auto& e = graph.arcs[graph.order[j]];
if (e.cap) value = std::min(value, height[e.to] + 1);
}
work += graph.start[v + 1] - graph.start[v];
--count[old], ++count[value], height[v] = value, current[v] = graph.start[v];
if (old < n && !count[old]) {
for (int u = 0; u < n; ++u) if (old < height[u] && height[u] < n) {
--count[height[u]], height[u] = n + 1, ++count[n + 1], current[u] = graph.start[u];
}
buckets();
break;
}
} else {
++work;
int i = graph.order[current[v]];
auto& e = graph.arcs[i];
if (!e.cap || height[v] != height[e.to] + 1) { ++current[v]; continue; }
Cap amount = Cap(std::min(excess[v], __uint128_t(e.cap)));
e.cap -= amount, graph.arcs[i ^ 1].cap += amount;
excess[v] -= amount, excess[e.to] += amount;
activate(e.to);
}
}
activate(v);
if (work >= threshold) global();
}
assert(excess[t] <= __uint128_t(std::numeric_limits<Cap>::max()));
return Cap(excess[t]);
}
}
#line 7 "graph/max_flow.hpp"
#include <type_traits>
template<class Cap = std::int64_t> class MaxFlow {
static_assert(std::is_integral_v<Cap> && !std::is_same_v<Cap, bool> && sizeof(Cap) <= 8);
flow_detail::Graph<Cap> graph_;
void vertex([[maybe_unused]] int v) const { assert(0 <= v && v < graph_.n); }
int index(int i) const { assert(0 <= i && std::size_t(i) < graph_.arcs.size() / 2); return 2 * i; }
public:
struct Edge { int from, to; Cap cap, flow; };
explicit MaxFlow(int n = 0) : graph_(n) {}
int size() const { return graph_.n; }
int edge_count() const { return int(graph_.arcs.size() / 2); }
void reserve(std::size_t m) { graph_.reserve(m); }
int add_edge(int from, int to, Cap cap) { return graph_.add_edge(from, to, cap); }
Edge get_edge(int i) const {
int j = index(i);
const auto& a = graph_.arcs[j];
const auto& b = graph_.arcs[j ^ 1];
return {b.to, a.to, Cap(a.cap + b.cap), b.cap};
}
std::vector<Edge> edges() const {
std::vector<Edge> result;
result.reserve(edge_count());
for (int i = 0; i < edge_count(); ++i) result.push_back(get_edge(i));
return result;
}
void change_edge(int i, Cap cap, Cap flow) {
assert(0 <= flow && flow <= cap);
int j = index(i);
graph_.arcs[j].cap = cap - flow, graph_.arcs[j ^ 1].cap = flow;
}
void reset_flow() { graph_.reset_flow(); }
Cap flow(int s, int t) {
vertex(s), vertex(t);
assert(s != t);
return flow_detail::push_relabel(graph_, s, t);
}
Cap flow(int s, int t, Cap limit) {
vertex(s), vertex(t);
assert(s != t && limit >= 0);
graph_.build();
auto& g = graph_;
std::vector<int> level(g.n), iter(g.n), queue(g.n);
struct Frame { int v; Cap limit, sent; };
std::vector<Frame> stack;
stack.reserve(g.n);
Cap total = 0;
while (total < limit) {
std::fill(level.begin(), level.end(), -1);
level[s] = 0;
int head = 0, tail = 0;
queue[tail++] = s;
while (head < tail && level[t] < 0) {
int v = queue[head++];
for (int i = g.start[v]; i < g.start[v + 1]; ++i) {
const auto& e = g.arcs[g.order[i]];
if (!e.cap || level[e.to] >= 0) continue;
level[e.to] = level[v] + 1;
queue[tail++] = e.to;
}
}
if (level[t] < 0) break;
std::copy_n(g.start.begin(), g.n, iter.begin());
stack.push_back({t, Cap(limit - total), Cap(0)});
while (!stack.empty()) {
auto& frame = stack.back();
int v = frame.v;
if (v == s || frame.sent == frame.limit || iter[v] == g.start[v + 1]) {
Cap sent = v == s ? frame.limit : frame.sent;
if (v != s && sent < frame.limit) level[v] = g.n;
stack.pop_back();
if (stack.empty()) { total += sent; break; }
auto& parent = stack.back();
int id = g.order[iter[parent.v]];
g.arcs[id].cap += sent, g.arcs[id ^ 1].cap -= sent;
parent.sent += sent;
if (parent.sent < parent.limit) ++iter[parent.v];
} else {
int id = g.order[iter[v]];
const auto& e = g.arcs[id];
if (level[e.to] < 0 || level[e.to] >= level[v] || !g.arcs[id ^ 1].cap) ++iter[v];
else stack.push_back({e.to, std::min<Cap>(frame.limit - frame.sent, g.arcs[id ^ 1].cap), Cap(0)});
}
}
}
return total;
}
std::vector<bool> min_cut(int s) {
vertex(s);
graph_.build();
std::vector<bool> visited(graph_.n);
std::vector<int> queue{ s };
visited[s] = true;
for (std::size_t head = 0; head < queue.size(); ++head) {
int v = queue[head];
for (int i = graph_.start[v]; i < graph_.start[v + 1]; ++i) {
const auto& e = graph_.arcs[graph_.order[i]];
if (e.cap && !visited[e.to]) visited[e.to] = true, queue.push_back(e.to);
}
}
return visited;
}
};