Skip to content

Commit 02a6878

Browse files
committed
add dinic.py in networking_flow folder
1 parent 2c15b8c commit 02a6878

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

networking_flow/dinic.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""
2+
Dinic's Algorithm for Maximum Flow Problem.
3+
Refined by Even and Itai for efficient blocking flow computation.
4+
5+
Description:
6+
The algorithm computes the maximum flow in a flow network.
7+
It constructs a level graph using BFS and then finds a blocking flow
8+
using DFS (incorporating the logic of Advance and Retreat).
9+
10+
Time Complexity: O(V^2 * E), or O(E * sqrt(V)) for unit networks.
11+
12+
Reference:
13+
- E. A. Dinic, "Algorithm for solution of a problem of maximum flow...", 1970.
14+
- S. Even and A. Itai, "Theoretical improvements in algorithmic efficiency...",
15+
1976.
16+
- https://en.wikipedia.org/wiki/Dinic%27s_algorithm
17+
"""
18+
19+
20+
class Dinic:
21+
def __init__(self, n: int):
22+
"""
23+
Initialize the Dinic algorithm with n nodes.
24+
Nodes are 0-indexed.
25+
"""
26+
self.n = n
27+
self.graph = [[] for _ in range(n)]
28+
self.level = []
29+
30+
def add_edge(self, u: int, v: int, capacity: int) -> None:
31+
"""
32+
Adds a directed edge with a capacity.
33+
Note: Stores indices to handle the residual edges efficiently.
34+
"""
35+
# Forward edge: [v, capacity, index_of_reverse_edge]
36+
self.graph[u].append([v, capacity, len(self.graph[v])])
37+
# Backward edge: [u, 0, index_of_forward_edge]
38+
self.graph[v].append([u, 0, len(self.graph[u]) - 1])
39+
40+
def bfs(self, source: int, sink: int) -> bool:
41+
"""
42+
Builds the Level Graph (L_G) using BFS.
43+
Corresponds to the INITIALIZE step in the Even-Itai refinement.
44+
Returns True if the sink is reachable, False otherwise.
45+
"""
46+
self.level = [-1] * self.n
47+
self.level[source] = 0
48+
# using list as queue to avoid importing collections.deque
49+
queue = [source]
50+
51+
while queue:
52+
u = queue.pop(0)
53+
for v, cap, _ in self.graph[u]:
54+
if cap > 0 and self.level[v] < 0:
55+
self.level[v] = self.level[u] + 1
56+
queue.append(v)
57+
58+
return self.level[sink] >= 0
59+
60+
def dfs(self, u: int, sink: int, flow: int, ptr: list) -> int:
61+
"""
62+
Finds a blocking flow in the Level Graph.
63+
Combines the ADVANCE and RETREAT steps.
64+
65+
Args:
66+
u: Current node.
67+
sink: Target node.
68+
flow: Current flow bottleneck.
69+
ptr: Current arc pointers (to implement 'Remove saturated edges').
70+
71+
Returns:
72+
The amount of flow pushed.
73+
"""
74+
if u == sink or flow == 0:
75+
return flow
76+
77+
for i in range(ptr[u], len(self.graph[u])):
78+
# 'ptr[u] = i' updates the current edge pointer (pruning L_G)
79+
# This is the "Current Arc Optimization"
80+
ptr[u] = i
81+
v, cap, rev_idx = self.graph[u][i]
82+
83+
# Condition: Edge exists in L_G (level[v] == level[u] + 1) and has capacity
84+
if self.level[v] == self.level[u] + 1 and cap > 0:
85+
# ADVANCE step: recurse to v
86+
pushed = self.dfs(v, sink, min(flow, cap), ptr)
87+
88+
if pushed > 0:
89+
# AUGMENT step: Update residual capacities
90+
self.graph[u][i][1] -= pushed
91+
self.graph[v][rev_idx][1] += pushed
92+
return pushed
93+
94+
# RETREAT step: If no flow can be pushed, this node is dead for this phase.
95+
return 0
96+
97+
def max_flow(self, source: int, sink: int) -> int:
98+
"""
99+
Computes the maximum flow from source to sink.
100+
"""
101+
max_f = 0
102+
# While we can build a Level Graph (source can reach sink)
103+
while self.bfs(source, sink):
104+
# ptr (pointer) array stores the index of the next edge to explore.
105+
# This implements the "Delete v" optimization efficiently
106+
ptr = [0] * self.n
107+
while True:
108+
pushed = self.dfs(source, sink, float("inf"), ptr)
109+
if pushed == 0:
110+
break
111+
max_f += pushed
112+
return max_f
113+
114+
115+
if __name__ == "__main__":
116+
117+
dn = Dinic(6)
118+
edges = [
119+
(0, 1, 16),
120+
(0, 2, 13),
121+
(1, 2, 10),
122+
(1, 3, 12),
123+
(2, 1, 4),
124+
(2, 4, 14),
125+
(3, 2, 9),
126+
(3, 5, 20),
127+
(4, 3, 7),
128+
(4, 5, 4),
129+
]
130+
for u, v, c in edges:
131+
dn.add_edge(u, v, c)
132+
133+
print(f"Maximum Flow: {dn.max_flow(0, 5)}")

0 commit comments

Comments
 (0)