Edmonds Karp と Push Relabel @ C++
コンテンツ
最大流アルゴリズムを実装した.
Edmonds Karpは増大道に沿ってフローを更新(大域的操作)に対して,
Push RelabelはPush か Relabel(局所的更新)のみ.
だから,楽に実装できると思ったら,実はたいして変わらなかった.
Edmons Karp
// O(VE^2), BFS = O(E), iteration = O(VE) template <class T> pair< T, EdgeProperty<T> > edmonds_karp(const Graph &g, //多重枝無し const EdgeProperty<T> &c, //capacity const Vertex &s, //source const Vertex &t //sink ) { const int n = g.succ.size(); // # of vertices T m = 0; // maximum flow value EdgeProperty<T> f; //flow for (Vertex u = 0; u < n; ++u) foreach (Vertex v, g.succ[u]) f[Edge(u, v)] = 0; // Initialize flow while (true) { // find augmenting path queue<Vertex> q; Vertex *p = new Vertex[n]; // previous vertex in path bool *r = new bool[n]; // r[i] <=> edge ?->i in path T d = INF; // TODO: INF<T> q.push(s), fill_n(p, n, -1), p[s] = s; while (!q.empty() && p[t] < 0) { Vertex u = q.front(); q.pop(); foreach (Vertex v, g.succ[u]) if (p[v] < 0 && f(u, v) < c(u, v)) // forward edge q.push(v), p[v] = u, r[v] = true, d = min(d, c(u, v) - f(u, v)); foreach (Vertex v, g.pred[u]) if (p[v] < 0 && 0 < f(v, u)) // backward edge q.push(v), p[v] = u, r[v] = false, d = min(d, f(v, u)); } if (p[t] < 0) return pair< T, EdgeProperty<T> >(m, f); // No augmenting path for (Vertex v = t; v != p[v]; v = p[v]) // udpate flow if (r[v]) f[Edge(p[v], v)] += d; else f[Edge(v, p[v])] -= d; m += d; } }
Push Relabel
// O(V^2E) // find excess vertex = O(1) // iteration (push or relabel) = O(V^2E) template <class T> pair< T, EdgeProperty<T> > stack_push_relabel(const Graph &g, //多重枝無し const EdgeProperty<T> &c, //capacity const Vertex &s, //source const Vertex &t //sink ) { const int n = g.succ.size(); // # of vertices EdgeProperty<T> f; //flow for (Vertex u = 0; u < n; ++u) foreach (Vertex v, g.succ[u]) f[Edge(u, v)] = 0; // initialize preflow int *h = new int[n]; fill_n(h, n, 0), h[s] = n; // initialize height T *e = new T[n]; fill_n(e, n, 0); //initialize excess stack<Vertex> stack; foreach(Vertex u, g.succ[s]) f[Edge(s, u)] = c(s, u), e[u] += f(s, u), stack.push(u); e[s] = INT_MAX>>1; LOOP: while (!stack.empty()) { Vertex u = stack.top(); stack.pop(); if (e[u] == 0) continue; int m = 2*n; // forall v in V, h[v] < 2*n foreach (Vertex v, g.succ[u]) // push forward if (f(u, v) < c(u, v)) { // (u, v) in Ef if (h[u] > h[v]) { T d = min(e[u], c(u, v) - f(u, v)); f[Edge(u, v)] += d, e[u] -= d, e[v] += d; if (e[u] > 0) stack.push(u); if (e[v] > 0 && v != t && v != s) stack.push(v); goto LOOP; // reset loop } else { m = min (m, h[v]); } } foreach (Vertex v, g.pred[u]) // push backward if (0 < f(v, u)) { // (v, u) in Ef if (h[u] > h[v]) { T d = min(e[u], f(v, u)); f[Edge(v, u)] -= d, e[u] -= d, e[v] += d; if (e[u] > 0) stack.push(u); if (e[v] > 0 && v != t && v != s) stack.push(v); goto LOOP; // reset loop } else { m = min (m, h[v]); } } h[u] = m + 1; //relabel(u); stack.push(u); } return pair< T, EdgeProperty<T> >(e[t], f); }
頂点から出て行く枝と頂点に入る枝を別々のリストで管理しているから,ソースが冗長になっている.
これをひとつのリストにまとめてしまえば,枝の向きを気にしなくて良くなるので,コードはもう少し簡潔になる.
しかし,有効グラフなのに枝の向きを無視するのはちょっと.
枝の容量とあわせて,向きが分かれば良いという立場もあるかもしれない.
しかし,グラフと容量は別ものだから(常に両方あるとは限らない),グラフだけでも,枝の向きが分かるほうが
精神的には落ち着く.
最後に priority queue を使って,ラベルの大きいものから処理していくコード.
多分,正しく実装できているはず(ラベル最大のものから処理しているはず).
// hightest label template <class T> pair< T, EdgeProperty<T> > queue_push_relabel(const Graph &g, //多重枝無し const EdgeProperty<T> &c, //capacity const Vertex &s, //source const Vertex &t //sink ) { const int n = g.succ.size(); // # of vertices EdgeProperty<T> f; //flow for (Vertex u = 0; u < n; ++u) foreach (Vertex v, g.succ[u]) f[Edge(u, v)] = 0; // initialize preflow int *h = new int[n]; fill_n(h, n, 0), h[s] = n; // initialize height T *e = new T[n]; fill_n(e, n, 0); //initialize excess typedef pair<int, Vertex> Data; priority_queue<Data> que; foreach(Vertex u, g.succ[s]) f[Edge(s, u)] = c(s, u), e[u] += f(s, u), que.push(Data(0, u)); e[s] = INT_MAX>>1; LOOP: while (!que.empty()) { Vertex u = que.top().second; que.pop(); if (e[u] == 0) continue; int m = 2*n; // forall v in V, h[v] < 2*n foreach (Vertex v, g.succ[u]) // push forward if (f(u, v) < c(u, v)) { // (u, v) in Ef if (h[u] > h[v]) { T d = min(e[u], c(u, v) - f(u, v)); f[Edge(u, v)] += d, e[u] -= d, e[v] += d; if (e[u] > 0) que.push(Data(h[u], u)); if (e[v] > 0 && v != t && v != s) que.push(Data(h[v], v)); goto LOOP; // reset loop } else { m = min (m, h[v]); } } foreach (Vertex v, g.pred[u]) // push backward if (0 < f(v, u)) { // (v, u) in Ef if (h[u] > h[v]) { T d = min(e[u], f(v, u)); f[Edge(v, u)] -= d, e[u] -= d, e[v] += d; if (e[u] > 0) que.push(Data(h[u], u)); if (e[v] > 0 && v != t && v != s) que.push(Data(h[v], v)); goto LOOP; // reset loop } else { m = min (m, h[v]); } } h[u] = m + 1; //relabel(u); que.push(Data(h[u], u)); } return pair< T, EdgeProperty<T> >(e[t], f); }
# ラベル付き break ぐらいあっても良いのに.
作成者 Toru Mano
最終更新時刻 2023-01-01 (c70d5a1)