最大フローを求めるアルゴリズム

Ford-Fulkerson法をベースに、最大フローにおける経路探索の深さ優先と幅優先の速度の違いをAOJで比較した。基本的なアルゴリズムを試したいときにAOJは便利。

幅優先を使ったときは、特にEdmonds-Karpのアルゴリズムと呼ばれ、計算量はO(VE2)である。

AOJの実行時間を比較結果は以下。思ったより差がでた。

  • 深さ優先:0.99s
  • 幅優先:0.03s

時間のかかっている問題(#27)をピックアップすると、フロー増加操作の繰り返し回数が以下の通り、実際結構違っていた。

  • 深さ優先:1664
  • 幅優先:21

コードは以下。ちなみに深さ優先はほとんど蟻本のC++を移植しただけで、幅優先は適当に組んだもの。幅優先のほうが経路のトレースバックとかは一部ナイーブな処理が残っている。

from collections import deque
import sys
sys.setrecursionlimit(10**7)

INF = 10 ** 15

V, E = [int(v) for v in input().split()]

CAP = 1
REV = 2

G = [[] for _ in range(V)]

for i in range(E):
    u, v, c = [int(v) for v in input().split()]
    rv = len(G[v])
    ru = len(G[u])
    G[u].append([v, c, rv])
    G[v].append([u, 0, ru])

def dfs(v, t):
    closed = [False] * V
    return _dfs(v, t, INF, closed)

def _dfs(v, t, f, closed):
    if v == t:
        return f
    closed[v] = True
    for e in G[v]:
        v1 = e[0]
        if closed[v1] or e[CAP] <= 0:
            continue
        d = _dfs(v1, t, min(f, e[CAP]), closed)

        if d <= 0:
            continue

        e[CAP] -= d
        G[v1][e[REV]][CAP] += d
        return d
    return 0

def bfs(v0, t):
    if v0 == t:
        return INF

    closed = [0] * V
    closed[v0] = INF

    q = deque([v0])
    path = [-1] * V
    path[v0] = -2

    while len(q) > 0:
        v = q.popleft()
        c = closed[v]
        for e in G[v]:
            v1 = e[0]
            if e[CAP] <= 0:
                continue
            c1 = min(c, e[CAP])
            if closed[v1] != 0:
                continue
            path[v1] = v
            closed[v1] = c1
            if v1 == t:
                traceback(t, path, c1)
                return c1
            q.append(v1)
    return 0

def traceback(p, path, c):
    p0 = p
    while path[p0] >= 0:
        p = path[p0]
        for e in G[p]:
            if e[0] != p0:
                continue
            e[CAP] -= c
            G[p0][e[REV]][CAP] += c
            break
        p0 = p

def max_flow(s, t):
    flow = 0
    count = 0
    while True:
        f = bfs(s, t)
        #f = dfs(s, t)
        if f == 0:
            return flow
        flow += f
        count += 1


print(max_flow(0, V - 1))