アルゴリズム
フォードファルカーソン法

フォードファルカーソン法

キーワード

概要

有向グラフ GG と各辺の非負の容量 uu と始点 ss と終点 tt からなるネットワーク [G;s,t,u][G;s,t,u] において、ss から tt への最大流を求めるアルゴリズム。

増大路アルゴリズムとも呼ばれる。

ネットワークとフロー xx に対して、残余ネットワーク [G;x][G;x] を以下のように定義する。

  • 各辺 (u,v)(u,v) に対して、x(u,v)<u(e)x(u,v) < u(e) ならば、 uu から vv への容量 uu からフロー x(u,v)x(u,v) を引いた値を容量とする辺 (u,v)(u,v) を追加する。
  • 各辺 (v,u)(v,u) に対して、x(u,v)>0x(u,v) > 0 ならば、フロー x(u,v)x(u,v) を容量とする辺 (v,u)(v,u) を追加する。
  1. 全ての辺のフローを 0 に初期化する。
  2. フロー値 FF を 0 に初期化する。
  3. ss から tt へのパスが存在する限り、以下の操作を繰り返す。
    1. 残余ネットワークにおいて、ss から tt へのパス(フロー追加路)を見つける。
    2. そのパスにおける最小容量 Δ\Deltaを求める。
    3. そのパスにおける各辺 (u,v)(u,v) に対して、x(u,v)x(u,v)+Δx(u,v) \leftarrow x(u,v) + \Delta または x(v,u)x(v,u)Δx(v,u) \leftarrow x(v,u) - \Delta とする。
    4. フロー値 FF+ΔF \leftarrow F + \Delta とする。

フロー追加路の探索には深さ優先探索を用いることが多い。

計算量

辺の数を EE 、最大流を FF とすると、計算量は O(FE)\mathrm{O}(F \cdot E) である。

応用例

  • 2 部マッチング
  • 最小カット
  • 最小費用流
  • 配送の最適化
  • 通信ネットワークの最適化

実装

type Edge struct {
	to, cap, rev int // to: 行き先, cap: 容量, rev: 逆辺のインデックス
}
 
func main() {
	var n, m int
	scanIntVariables(&n, &m)
	G := make([][]Edge, n+1) // 残余グラフ
	for i := 0; i < m; i++ {
		var a, b, c int
		scanIntVariables(&a, &b, &c)
		G[a] = append(G[a], Edge{b, c, len(G[b])})
		G[b] = append(G[b], Edge{a, 0, len(G[a]) - 1})
	}
	visited := make([]bool, n+1)
	var dfs func(v, t, d int) int
	dfs = func(v, t, f int) int {
		if v == t {
			return f
		}
		visited[v] = true
		for i := 0; i < len(G[v]); i++ {
			if G[v][i].cap == 0 {
				continue
			}
			if visited[G[v][i].to] {
				continue
			}
			del := dfs(G[v][i].to, t, min(f, G[v][i].cap)) // パスの最小容量
			if del > 0 {
				G[v][i].cap -= del
				G[G[v][i].to][G[v][i].rev].cap += del
				return del
			}
		}
		return 0
	}
 
	ans := 0
	for { // 増加路がなくなるまで繰り返す
		visited = make([]bool, n+1)
		d := dfs(1, n, math.MaxInt32)
		if d == 0 {
			break
		}
		ans += d
	}
	writeLine(ans)
}

例題

引用