-
-
Notifications
You must be signed in to change notification settings - Fork 51.1k
Expand file tree
/
Copy pathminimum_cut.py
More file actions
101 lines (78 loc) · 3.01 KB
/
Copy pathminimum_cut.py
File metadata and controls
101 lines (78 loc) · 3.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""
Minimum cut of a flow network via the Ford-Fulkerson algorithm.
The max-flow min-cut theorem says the value of a maximum flow from the source to
the sink equals the total capacity of the edges in a minimum s-t cut -- the
cheapest set of edges whose removal disconnects the sink from the source. This
module finds those cut edges: it runs Ford-Fulkerson to build the residual
graph, then reports every original edge that goes from a vertex still reachable
from the source to a vertex that is not.
Reference: https://en.wikipedia.org/wiki/Minimum_cut
See also: https://en.wikipedia.org/wiki/Max-flow_min-cut_theorem
"""
test_graph = [
[0, 16, 13, 0, 0, 0],
[0, 0, 10, 12, 0, 0],
[0, 4, 0, 0, 14, 0],
[0, 0, 9, 0, 0, 20],
[0, 0, 0, 7, 0, 4],
[0, 0, 0, 0, 0, 0],
]
def bfs(graph: list[list[int]], source: int, sink: int, parent: list[int]) -> bool:
"""
Return True if the ``sink`` is reachable from the ``source`` in the
residual ``graph``, recording the traversal tree in ``parent``.
>>> bfs(test_graph, 0, 5, [-1] * 6)
True
>>> bfs([[0, 0], [0, 0]], 0, 1, [-1, -1])
False
"""
visited = [False] * len(graph)
queue = [source]
visited[source] = True
while queue:
node = queue.pop(0)
for neighbor in range(len(graph[node])):
if visited[neighbor] is False and graph[node][neighbor] > 0:
queue.append(neighbor)
visited[neighbor] = True
parent[neighbor] = node
return visited[sink]
def mincut(graph: list[list[int]], source: int, sink: int) -> list[tuple[int, int]]:
"""
Return the edges of a minimum s-t cut as ``(from, to)`` tuples.
The input ``graph`` is an adjacency matrix of capacities and is left
unchanged (the algorithm works on an internal copy).
>>> mincut(test_graph, source=0, sink=5)
[(1, 3), (4, 3), (4, 5)]
The capacities of the cut edges sum to the maximum flow (23 here):
>>> sum(test_graph[u][v] for u, v in mincut(test_graph, 0, 5))
23
A single saturated edge is its own minimum cut:
>>> mincut([[0, 7], [0, 0]], source=0, sink=1)
[(0, 1)]
"""
residual = [row[:] for row in graph] # work on a copy; keep the input intact
parent = [-1] * (len(residual))
res = []
while bfs(residual, source, sink, parent):
path_flow = float("inf")
s = sink
while s != source:
# Find the minimum residual capacity along the augmenting path.
path_flow = min(path_flow, residual[parent[s]][s])
s = parent[s]
v = sink
while v != source:
u = parent[v]
residual[u][v] -= path_flow
residual[v][u] += path_flow
v = parent[v]
for i in range(len(graph)):
for j in range(len(graph[0])):
if graph[i][j] > 0 and residual[i][j] == 0:
res.append((i, j))
return res
if __name__ == "__main__":
from doctest import testmod
testmod()
print(mincut(test_graph, source=0, sink=5))