アルゴリズム
ディニッツ法

ディニッツ法

キーワード

概要

最大流問題のアルゴリズムの一つ。

フォードファルカーソン法エドモンズカープ法よりも高速である。

エドモンズカープ法と同様に、最短のパスを探索することで、最大流を求める。

エドモンズカープ法は、毎回最短のパスを探索するが、ディニッツ法では、一度レベルネットワーク(sts-t 最短路 DAG)を構築し、その上で最短のパスを探索したのち、ブロックフローをまとめて更新するという手法を取る。

計算量

O(N2M)\mathrm{O}(N^2M) だが、実際にはもっと早い。

最短経路 DAG 上の DFS は、順番に辿ると長さ N1N-1 以下の最短パスが得られるため O(N)\mathrm{O}(N) で、一度増加路を通ると、少なくとも一本の辺が飽和するので、DFS が O(M)\mathrm{O}(M) 回呼ばれる。

最短経路長は、ブロックフローを更新するたびに、少なくとも 11 増加し、最大で N1N-1 まで増加するため、BFS が O(N)\mathrm{O}(N) 回呼ばれる。

したがって、全体の計算量は O(N2M)\mathrm{O}(N^2M) となる。

応用例

実装

type Edge struct {
	to, cap, rev int
}
 
func main() {
	var n, m int
	scanIntVariables(&n, &m)
	G := make([][]*Edge, n+1)
	L := make([]int, n+1)
	C := make([]int, 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})
	}
	s := 1
	t := n
	bfs := func() {
		for i := 0; i <= n; i++ {
			L[i] = -1
		}
		que := queue.New[int]()
		que.Push(s)
		L[s] = 0
		for !que.Empty() {
			v := que.Pop()
			for _, e := range G[v] {
				if e.cap > 0 && L[e.to] == -1 {
					L[e.to] = L[v] + 1
					que.Push(e.to)
				}
			}
		}
	}
	var dfs func(v, f int) int
	dfs = func(v, f int) int {
		if v == t {
			return f
		}
		for _, e := range G[v] {
			if e.cap > 0 && L[v] < L[e.to] {
				d := dfs(e.to, min(f, e.cap))
				if d > 0 {
					e.cap -= d
					G[e.to][e.rev].cap += d
					return d
				}
			}
		}
		return 0
	}
	ans := 0
	for {
		bfs()
		if L[t] == -1 {
			break
		}
		for {
			for i := 0; i <= n; i++ {
				C[i] = 0
			}
			f := dfs(s, 1<<60)
			if f == 0 {
				break
			}
			ans += f
		}
	}
	writeLine(ans)
}

ライブラリ化したもの

func NewMaximumFlow(size int) *MaximumFlow {
	return &MaximumFlow{
		graph:    make([][]*Edge, size+1),
		level:    make([]int, size+1),
		capacity: make([]int, size+1),
		size:     size,
	}
}
 
func (mf *MaximumFlow) AddEdge(from, to, cap int) {
	mf.graph[from] = append(mf.graph[from], &Edge{to, cap, len(mf.graph[to])})
	mf.graph[to] = append(mf.graph[to], &Edge{from, 0, len(mf.graph[from]) - 1})
}
 
func (mf *MaximumFlow) MaxFlow(s, t int) int {
	bfs := func() {
		for i := 0; i <= mf.size; i++ {
			mf.level[i] = -1
		}
		que := queue.New[int]()
		que.Push(s)
		mf.level[s] = 0
		for !que.Empty() {
			v := que.Pop()
			for _, e := range mf.graph[v] {
				if e.cap > 0 && mf.level[e.to] == -1 {
					mf.level[e.to] = mf.level[v] + 1
					que.Push(e.to)
				}
			}
		}
	}
	var dfs func(v, f int) int
	dfs = func(v, f int) int {
		if v == t {
			return f
		}
		for _, e := range mf.graph[v] {
			if e.cap > 0 && mf.level[v] < mf.level[e.to] {
				d := dfs(e.to, min(f, e.cap))
				if d > 0 {
					e.cap -= d
					mf.graph[e.to][e.rev].cap += d
					return d
				}
			}
		}
		return 0
	}
	flow := 0
	for {
		bfs()
		if mf.level[t] == -1 {
			break
		}
		for {
			for i := 0; i <= mf.size; i++ {
				mf.capacity[i] = 0
			}
			f := dfs(s, 1<<60)
			if f == 0 {
				break
			}
			flow += f
		}
	}
	return flow
}
 
func main() {
	var n, m int
	scanIntVariables(&n, &m)
	mf := NewMaximumFlow(n + 1)
	for i := 0; i < m; i++ {
		var a, b, c int
		scanIntVariables(&a, &b, &c)
		mf.AddEdge(a, b, c)
	}
	writeLine(mf.MaxFlow(1, n))
}

例題

引用

Dinic 法とその時間計算量 (opens in a new tab)

【C++】Dinic 法の実装 (opens in a new tab)