我最早学网络流时,觉得 Edmonds-Karp 就够用了:BFS 找增广路,逻辑简单,代码也短。直到我拿它去跑一个上千节点、万级边的二分图匹配,发现每次迭代只能榨出一条路,慢得让人怀疑人生。后来换用 Dinic,代码量其实没增加太多,速度却有了质的飞跃。
Dinic 的核心就做了两件事:分层图 和 阻塞流。用 BFS 给残量网络分层,再在分层有向无环图上用 DFS 一次性抽出所有能找到的增广路——这样一轮下来就清空了当前层级的可行流,远胜每次只找一条路的打法。加上一个不起眼的 当前弧优化,能让 DFS 跳过已经饱和的边,避免重复试探。
下面是我常用的一个实现,带完整的最小割计算。
from collections import deque
class Dinic:
def __init__(self, n, source, sink):
self.n = n
self.source = source
self.sink = sink
# 邻接表:每条边存 (目标, 剩余容量, 反向边索引)
self.graph = [[] for _ in range(n)]
self.level = [-1] * n
self.ptr = [0] * n
def add_edge(self, u, v, capacity):
forward = (v, capacity, len(self.graph[v]))
backward = (u, 0, len(self.graph[u]))
self.graph[u].append(forward)
self.graph[v].append(backward)
def bfs_level(self):
self.level = [-1] * self.n
q = deque()
self.level[self.source] = 0
q.append(self.source)
q:
u = q.popleft()
v, cap, _ .graph[u]:
.level[v] == - cap > :
.level[v] = .level[u] +
q.append(v)
v == .sink:
.level[.sink] != -
():
u == .sink:
flow_limit
.ptr[u] < (.graph[u]):
edge_idx = .ptr[u]
v, cap, rev_idx = .graph[u][edge_idx]
.level[v] == .level[u] + cap > :
min_flow = (flow_limit, cap)
aug_flow = .dfs_flow(v, min_flow)
aug_flow > :
.graph[u][edge_idx] = (v, cap - aug_flow, rev_idx)
rev_v, rev_cap, rev_rev_idx = .graph[v][rev_idx]
.graph[v][rev_idx] = (rev_v, rev_cap + aug_flow, rev_rev_idx)
aug_flow
.ptr[u] +=
():
total_flow =
.bfs_level():
.ptr = [] * .n
:
flow = .dfs_flow(.source, ())
flow == :
total_flow += flow
total_flow
():
visited = [] * .n
q = deque()
q.append(.source)
visited[.source] =
q:
u = q.popleft()
v, cap, _ .graph[u]:
visited[v] cap > :
visited[v] =
q.append(v)
S = [i i (.n) visited[i]]
T = [i i (.n) visited[i]]
cut_capacity =
u S:
v, cap, rev_idx .graph[u]:
v T:
rev_cap = .graph[v][rev_idx][]
cut_capacity += cap + rev_cap
S, T, cut_capacity
__name__ == :
n =
dinic = Dinic(n, , )
dinic.add_edge(, , )
dinic.add_edge(, , )
dinic.add_edge(, , )
dinic.add_edge(, , )
dinic.add_edge(, , )
(, dinic.max_flow())
S, T, cut = dinic.min_cut()
()
()
()


