dinic

Dinic's Algorithm — Max Flow

Overview

Dinic's algorithm finds max flow in O(V2E)O(V^2 E) by repeatedly:

  1. Build a level graph via BFS (assign distance labels from ss)
  2. Find blocking flows via DFS (saturate all shortest paths at once)

Each BFS phase increases the shortest ss-tt distance by at least 1, so at most V1V-1 phases.

Time: O(V2E)vs Edmonds-Karp O(VE2)\text{Time: } O(V^2 E) \quad \text{vs Edmonds-Karp } O(VE^2)

Walkthrough

Step 1 / 19

Why Dinic's is Faster

Key optimization: the iter[] array (current-arc optimization) ensures each edge is examined at most once per DFS phase.

Implementation

from collections import deque

class Dinic:
    def __init__(self, n):
        self.n = n
        self.graph = [[] for _ in range(n)]

    def add_edge(self, u, v, cap):
        self.graph[u].append([v, cap, len(self.graph[v])])
        self.graph[v].append([u, 0, len(self.graph[u]) - 1])

    def bfs(self, s, t):
        """Build level graph. Return True if t reachable."""
        self.level = [-1] * self.n
        self.level[s] = 0
        q = deque([s])
        while q:
            u = q.popleft()
            for v, cap, _ in self.graph[u]:
                if cap > 0 and self.level[v] < 0:
                    self.level[v] = self.level[u] + 1
                    q.append(v)
        return self.level[t] >= 0

    def dfs(self, u, t, pushed):
        """Find blocking flow via DFS with current-arc optimization."""
        if u == t:
            return pushed
        while self.iter[u] < len(self.graph[u]):
            e = self.graph[u][self.iter[u]]
            v, cap, rev = e
            if cap > 0 and self.level[v] == self.level[u] + 1:
                d = self.dfs(v, t, min(pushed, cap))
                if d > 0:
                    e[1] -= d
                    self.graph[v][rev][1] += d
                    return d
            self.iter[u] += 1
        return 0

    def max_flow(self, s, t):
        flow = 0
        while self.bfs(s, t):          # Phase: build level graph
            self.iter = [0] * self.n    # Reset current-arc pointers
            while True:                 # Find all blocking flows
                f = self.dfs(s, t, float('inf'))
                if f == 0:
                    break
                flow += f
        return flow