Dinic’s Algorithm (Maximum flow problem)
概要
最大流問題を解くDinic’s Algorithmの実装。
合わせて、辺の容量増加と容量減少にも対応できる実装が含まれる。
実装
#include<iostream>
#include<vector>
#include<map>
#include<queue>
using namespace std;
using P = pair<int, int>;
// Dinicアルゴリズム
// コンストラクタでnとsinkとsourceを決める。
// add_edge(s, t, cap)で辺追加。
// max_flow()で最大流を求める。
// グラフの変形がある場合は
// - inc_edge(s, t, delta)[容量増加]
// - dec_edge(s, t, delta)[容量減少]
// で変形を行う。
// 変形後はmax_flow()を使って求める。
struct Edge {
int to, cap, rev;
Edge(int t, int c, int r) : to(t), cap(c), rev(r) {}
Edge() {}
};
#define MAX_N 1005
class Dinic {
static const int inf = 1e9;
vector<Edge> g[MAX_N];
int level[MAX_N], iter[MAX_N];
int dist[MAX_N];
int flow, n;
int st, en;
map<P, int> edges;
void bfs(int s) {
for(int i=0; i<n; ++i) level[i] = -1, dist[i] = inf;
queue<int> que;
level[s] = 0;
que.push(s);
while(!que.empty()) {
int v = que.front(); que.pop();
for(int i=0; i<g[v].size(); ++i) {
Edge &e = g[v][i];
if(e.cap > 0 && level[e.to] < 0) {
level[e.to] = level[v] + 1;
dist[e.to] = min(e.cap, dist[v]);
que.push(e.to);
}
}
}
}
int dfs(int v, int t, int f) {
if(v == t) return f;
for(int &i=iter[v]; i<g[v].size(); ++i) {
Edge &e = g[v][i];
if(e.cap > 0 && level[v] < level[e.to]) {
int d = dfs(e.to, t, min(f, e.cap));
if(d > 0) {
e.cap -= d;
g[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
public:
Dinic(int n, int s, int t) {
flow = 0;
st = s; en = t;
this->n = n;
}
// 有効辺の追加
void add_edge(int from, int to, int cap) {
edges[P(from, to)] = g[from].size();
g[from].push_back(Edge(to, cap, g[to].size()));
edges[P(to, from)] = g[to].size();
g[to].push_back(Edge(from, 0, g[from].size()-1));
}
// 無向辺の追加
void add_multi_edge(int v1, int v2, int cap) {
edges[P(v1, v2)] = g[v1].size();
g[v1].push_back(Edge(v2, cap, g[v2].size()));
edges[P(v2, v1)] = g[v2].size();
g[v2].push_back(Edge(v1, cap, g[v1].size()-1));
}
// 最大流を求める
int max_flow(int s = -1, int t = -1, int rest = -1) {
if(s == -1) s = st;
if(t == -1) t = en;
int su = 0;
while(rest) {
bfs(s);
if(level[t] < 0) break;
for(int i=0; i<n; ++i) iter[i] = 0;
int f;
while(rest && (f = dfs(s, t, rest == -1 ? inf : rest)) > 0) {
if(rest == -1) {
flow += f;
} else {
rest -= f;
}
su += f;
}
}
return rest == -1 ? flow : su;
}
// 容量増加
void inc_edge(int s, int t, int c) {
P key = P(s, t);
if(edges.find(key) != edges.end()) {
g[s][edges[key]].cap += c;
} else {
add_edge(s, t, c);
}
}
// 容量減少
void dec_edge(int s, int t, int c) {
P key = P(s, t);
if(edges.find(key) != edges.end()) {
int cap = g[s][edges[key]].cap;
if(c <= cap) {
g[s][edges[key]].cap -= c;
} else {
int rest = c - cap;
g[s][edges[key]].cap = 0;
g[t][edges[P(t, s)]].cap -= rest;
bfs(s);
if(level[t] >= 0) {
// s -> t の残余グラフがあれば流す
int ff = max_flow(s, t, rest);
rest -= ff;
}
if(rest) {
// end -> t, s -> start の残余グラフを流して
// 全体の流す量を減らす
int f1 = max_flow(en, t, rest);
int f2 = max_flow(s, st, rest);
//assert(f1 == f2 && f1 == rest && f2 == rest);
flow -= rest;
}
}
}
}
void debug() {
for(int i=0; i<n; ++i) {
for(int j=0; j<n; ++j) {
if(i == j) continue;
if(edges.find(P(i, j)) != edges.end()) {
Edge &e = g[i][edges[P(i, j)]];
cout << i << " " << j << " " << e.cap << endl;
}
}
}
}
};
Verified
-
AOJ: "2313: Box Witch": source (C++, 0.32sec)
参考
-
秋葉拓哉, 岩田陽一, and 北川宜稔. "プログラミングコンテストチャレンジブック." (2010).