連結な無向重みつきグラフ G = (V, E) に対し,重みつき木 T が Gomory-Hu 木,またはカット木であるとは,次の二条件を満たすことをいう.
任意のグラフに対し,Gomory-Hu 木が存在することが証明できる.
たくさんの点対に対して最小カットを計算しなければならない場合,あらかじめ Gomory-Hu 木を計算しておけば,G の最小カットのかわりに T の最小カットを求めればよくなるため,効率的に計算できる(木の最小カットは経路上の最小重みなので簡単に求まる.
Gomory-Hu 木を計算するアルゴリズムは,まず Gomory と Hu によって,頂点を圧縮しながら最大流を O(V) 回計算するものが提案された.その後 Gusfield によってアルゴリズムが整理され,陽に頂点の圧縮を行なう必要の無いアルゴリズムが提案された.以下の実装は Gusfield の提案したものによる.
O(V MAXFLOW).
#define RESIDUE(s,t) (capacity[s][t]-flow[s][t]) Graph cutTree(const Graph &g) { int n = g.size(); Matrix capacity(n, Array(n)), flow(n, Array(n)); REP(u,n) FOR(e,g[u]) capacity[e->src][e->dst] += e->weight; vector<int> p(n), prev; vector<Weight> w(n); for (int s = 1; s < n; ++s) { int t = p[s]; // max-flow(s, t) REP(i,n) REP(j,n) flow[i][j] = 0; Weight total = 0; while (1) { queue<int> Q; Q.push(s); prev.assign(n, -1); prev[s] = s; while (!Q.empty() && prev[t] < 0) { int u = Q.front(); Q.pop(); FOR(e,g[u]) if (prev[e->dst] < 0 && RESIDUE(u, e->dst) > 0) { prev[e->dst] = u; Q.push(e->dst); } } if (prev[t] < 0) goto esc; Weight inc = INF; for (int j = t; prev[j] != j; j = prev[j]) inc = min(inc, RESIDUE(prev[j], j)); for (int j = t; prev[j] != j; j = prev[j]) flow[prev[j]][j] += inc, flow[j][prev[j]] -= inc; total += inc; } esc:w[s] = total; // make tree REP(u, n) if (u != s && prev[u] != -1 && p[u] == t) p[u] = s; if (prev[p[t]] != -1) p[s] = p[t], p[t] = s, w[s] = w[t], w[t] = total; } Graph T(n); // (s, p[s]) is a tree edge of weight w[s] REP(s, n) if (s != p[s]) { T[ s ].push_back( Edge(s, p[s], w[s]) ); T[p[s]].push_back( Edge(p[s], s, w[s]) ); } return T; } // Gomory-Hu tree を用いた最大流 O(n) Weight maximumFlow(const Graph &T, int u, int t, int p = -1, Weight w = INF) { if (u == t) return w; Weight d = INF; FOR(e, T[u]) if (e->dst != p) d = min(d, maximumFlow(T, e->dst, t, u, min(w, e->weight))); return d; }
Last Modified: 2007.10.18 00:43:09.