Purpose
Max-flow and min-cut sit behind a large family of reductions. This note covers the core algorithm (Ford-Fulkerson), the classic reduction from bipartite matching, an image segmentation formulation, and four worked problems that reduce to flow. Two of the problem writeups (P1 and P3) reference results proved in course section or lecture that this note does not reproduce, which is why it carries a needs-review status.
Max Flow/Min Cut
Max flow and min cut show up wherever you route things through capacity-limited networks or split objects into two groups at minimum cost:
- Transportation networks: flow of goods, data, or people through a network
- Telecommunications: the min cut is exactly the number of edges you must remove to disconnect servers and
- Image segmentation: partitioning an image into regions
- Data mining: clustering and classification
Use min cut when you want a partition of objects into two sets minimizing some cost. Use max flow for optimal routing. If the objects carry a natural ordering, also consider dynamic programming, and for problems on trees, try greedy (inducting on leaves) or dynamic programming on subtrees.
Given a graph with edge capacities and vertices , the max-flow problem asks for the maximum value of a flow from to . The max-flow min-cut theorem says this value equals the minimum capacity over all cuts separating from , where for a cut with :
Max-flow min-cut theorem
The maximum value of an - flow equals the minimum capacity over all - cuts. Ford-Fulkerson proves both directions at once: when no augmenting path remains, the vertices reachable from in the residual graph form the side of a cut whose capacity equals the current flow value.
Ford Fulkerson Algorithm
The residual graph represents remaining capacity. If edge has capacity and current flow , the residual capacity forward is , and there is a backward residual edge with capacity that lets the algorithm undo flow it committed earlier.
An augmenting path is a path from to in using only edges with positive residual capacity. Ford-Fulkerson repeatedly finds an augmenting path and pushes the bottleneck (minimum residual capacity along the path) through it, until no augmenting path exists. At that point the flow is maximum, and the vertices reachable from in the residual graph form the side of a minimum cut.
Pushing flow along a forward edge increases the flow on that edge. Pushing flow along a backward residual edge decreases the flow on the corresponding forward edge, rerouting flow that an earlier iteration placed badly.
The network below shows the backward edge doing that work. Every capacity is and the max flow is :
flowchart LR s((s)) -->|1| a((a)) s -->|1| b((b)) a -->|1| b a -->|1| t((t)) b -->|1| t style s fill:#e8f5e9,stroke:#2e7d32 style t fill:#e3f2fd,stroke:#1565c0
A DFS that first augments along saturates and leaves a flow of value . The second augmenting path is : it crosses the backward residual edge , canceling the unit on . The final flow routes and with nothing on the middle edge.
from collections import defaultdict
def ford_fulkerson(G, s, t, c):
# G: adjacency lists, c[u][v]: capacity of edge (u, v)
# r holds residual capacities, including backward edges
r = defaultdict(dict)
for u in G:
for v in G[u]:
r[u][v] = c[u][v]
r[v].setdefault(u, 0)
def find_augmenting_path():
stack = [(s, [s])]
seen = {s}
while stack:
u, path = stack.pop()
if u == t:
return path
for v, cap in r[u].items():
if cap > 0 and v not in seen:
seen.add(v)
stack.append((v, path + [v]))
return None
flow = 0
while True:
path = find_augmenting_path()
if path is None:
return flow
bottleneck = min(r[u][v] for u, v in zip(path, path[1:]))
for u, v in zip(path, path[1:]):
r[u][v] -= bottleneck
r[v][u] += bottleneck
flow += bottleneckRunning Time
Assume all capacities are integers between and . Invariantly, every flow value and every residual capacity remains an integer throughout the algorithm.
Theorem: the algorithm terminates after at most iterations, where is an optimal flow.
Only produces flow, and at most edges leave , each with capacity at most , so . Each iteration increases the flow value by at least (flows stay integral), which bounds the iteration count.
Each iteration finds an augmenting path via DFS in , giving an overall runtime of , or more generally .
The bound is pseudo-polynomial
occupies only bits of input, so can be exponential in the input size. The diamond network above with capacities on the outer edges and on realizes the blowup: a path search that keeps routing through the middle edge alternates with , gaining unit per iteration and taking iterations to reach the max flow of .
Maximum Matching
Given an undirected graph , find the matching with largest cardinality, where a matching is a set of edges such that each vertex touches at most one edge in . This is solvable in polynomial time in general; here I only cover the case where is bipartite.
Bipartite Maximum Matching
Given an undirected bipartite graph , find the maximum matching .
Add vertices , with edges of capacity for , and of capacity for . All original edges get capacity and are oriented from to .
The maximum flow value equals the maximum matching size, and the minimum cut yields the minimum vertex cover (this equality of matching and cover sizes in bipartite graphs is Konig’s theorem).
Proof: Let be a maximum matching in , and the value of a maximum integer flow in the constructed graph . We prove by showing each bounds the other.
: it suffices to exhibit a flow of value .
- For every edge , set , and all other flows to .
- This flow is feasible: no capacity is exceeded because is a matching (each vertex is used once), and conservation holds at every internal vertex.
- Its value is , so the max flow is at least .
: given an integer flow of value , we construct a matching of size .
- Integer capacities guarantee an integer maximum flow exists.
- Every edge into and out of has capacity , so each vertex of receives at most unit and each vertex of sends at most unit.
- Every unit of flow travels a path with , and these paths share no internal vertices. The middle edges therefore form a matching of size .
Foreground/Background Segmentation
Label each pixel of an image as foreground or background. Let be the set of pixels and connect neighboring pixels.
- is the likelihood of pixel being in the foreground
- is the likelihood of pixel being in the background
- is the penalty for labeling one of and as foreground and the other as background, i.e. the penalty for a region boundary between them
Two forces pull on each label. Accuracy: if in isolation, prefer to label foreground. Smoothness: if many neighbors of are foreground, lean foreground for too.
Find a partition that maximizes
Min-cut minimizes, so negate the objective:
Negative terms are awkward in a cut formulation, so add the constant , which shifts every solution equally:
Since and , this equals
Add vertices and , an edge with weight for every pixel, an edge with weight for every pixel, and weight on every neighbor edge . The min-cut of this graph minimizes the shifted objective, giving the optimal partition .
P1 - Count Disjoint Paths
Given an undirected graph and disjoint vertex sets , design a polynomial time algorithm that outputs the maximum number of vertex-disjoint paths between vertices of and (every vertex in and every vertex in can be in at most one path).
Algorithm:
- Given , and
- Construct a directed graph with all undirected edges replaced by two directed edges . Add two vertices , , an edge for all , and an edge for all .
- Run the algorithm from section for the maximum number of vertex-disjoint paths in an unweighted directed graph. (That algorithm reduces to max-flow; this note treats it as a given.)
Correctness:
Let be an undirected graph, and , be disjoint subsets of .
Since we have an algorithm to count vertex-disjoint paths from to in a directed graph, it is enough to show the construction of preserves the answer: the number of vertex-disjoint paths in equals the number of vertex-disjoint paths in from vertices in to vertices in .
The algorithm adds both a forward and backward directed edge for every edge in , since is undirected and a path may traverse an edge in either direction. The paths counted must be vertex-disjoint (not counting and ), so at most one of the two directed copies of an edge is ever used across all counted paths, and adding both copies never inflates the count.
The added vertices and create a bijection between , a maximum set of vertex-disjoint paths in , and , a maximum set of vertex-disjoint paths from to in . Letting and denote arbitrary members, paths have these forms:
- for all paths
- for all paths
All paths in have some as their second vertex, since the only edges leaving go to , and some as their second-to-last vertex, since the only edges into come from . To show :
-
: suppose for contradiction . Prepend and append to every path in (possible because of the added edges) to get vertex-disjoint paths in . That contradicts being maximum.
-
: suppose for contradiction . Strip and from every path in . The stripped paths remain vertex-disjoint, start in , and end in , giving vertex-disjoint -to- paths in . That contradicts being maximum.
Running Time: constructing takes for duplicating and orienting edges, plus to add , , and their edges. The disjoint-paths algorithm runs in , so the total is , which is polynomial.
P2 - Number of Disjoint Paths
Given a directed unweighted graph , suppose there are edge-disjoint paths from to and edge-disjoint paths from to , for vertices . Prove that there are edge-disjoint paths from to .
Proof:
Construct a graph by adding vertices with edges , each with capacity , and give every original edge capacity .
The max flow from is : send unit along each of the edge-disjoint paths, fed by the capacity- edge and drained by . Similarly the max flow from is , using the edge-disjoint paths. Conservation holds at every vertex in both flows.
To get edge-disjoint paths from to , we exhibit a flow of value from . The edge-disjoint paths end in distinct capacity- edges entering , and the edge-disjoint paths leave through distinct capacity- edges out of . Route the units arriving at from the first family of paths out through the second family. Conservation at holds because units enter and units leave.
This flow travels from to entirely through capacity- edges, so its units decompose into edge-disjoint paths. The and path families may share edges with each other in general, but the decomposition of this single flow cannot reuse an edge, since each edge supports at most unit.
P3 - Minimum Vertex Cover, Maximum Independent Set (Bipartite)
In this exercise we give a polynomial time algorithm to find the minimum vertex cover and maximum independent set in a bipartite graph .
(a) Construct H
Let be a directed graph on vertices with each edge , where and , given . Add vertices , edges , and , with . Let these edge sets be and respectively.
(b) Construct S
Let be a min s-t cut in . I will construct a vertex cover such that . Define , , , .
The following lemmas will become useful:
- (1): the capacity of the min s-t cut of is upper bounded by
- Cutting all of separates from at capacity , and the min cut can only be smaller.
- (2): all have capacity (by construction).
- (3): no edges from exist
- By (1), is finite. Since all edges have , such an edge would put infinite capacity across the cut, contradicting (1).
- (4): (shown in lecture)
- By (3) and bipartiteness, every edge crossing the cut is either an edge into or an edge out of .
- By (2) each such edge has capacity , and there are of the first kind and of the second.
By (3), all edges of fall into three sets: between and , between and , and between and .
We can therefore select a vertex cover , since covers and , and covers and . By (4), . This is a vertex cover of size , though not yet shown minimum.
(c) Construct (A, B)
Let be a vertex cover of . I will construct an s-t cut in with .
Let and . Take and .
No infinite edge crosses this cut: an original edge crossing from to would need and , meaning edge has neither endpoint in , contradicting being a vertex cover.
The crossing edges are exactly the edges from into (capacity each, of them) and the edges from into (capacity each, of them). Therefore .
(d) Design the algorithm
- Given a bipartite graph
- Construct as described in (a), but use capacity instead of on edges between and
- Find the min s-t cut in using Ford-Fulkerson
- Let ,
- Return
Correctness:
Replacing with preserves lemma (3): a cut crossing such an edge has capacity at least , while the min cut has capacity at most , so no such edge crosses the min cut.
From (b), the returned is a vertex cover with . Suppose for contradiction is not minimum, i.e. some vertex cover has . From (c), yields an s-t cut with . That contradicts being a min cut.
The maximum independent set is the complement of the minimum vertex cover.
Running Time:
For a bipartite graph in adjacency list form with and , constructing takes : two added vertices, added edges, and capacity initialization.
With integer capacities, Ford-Fulkerson terminates in at most iterations with , each taking to find an augmenting path, for total.
Ford-Fulkerson outputs the min cut , and computing , , and their union takes with hash sets. The total runtime is , which is polynomial.
P4 - Knights
Given an chess board where some cells are removed, design a polynomial time algorithm to find the maximum number of knights that can be placed on the board such that no two knights attack each other.
Algorithm:
- Given , the set of removed cells , and board size
- Create an undirected graph where , the cells still present
- Add an edge whenever knights on those two cells attack each other, i.e. both cells are in and
- Find the minimum vertex cover of using the algorithm from P3 (knight moves always connect cells of opposite color, so is bipartite with the sides given by the standard checkerboard coloring)
- Return
Running Time:
Creating takes , assuming an boolean grid makes checking .
Computing iterates over all cells and checks the at most cells a knight there could attack, so total. The graph has and .
Finding the minimum vertex cover via P3 costs , and returning is constant time on top of that. The total running time is , which is polynomial.
Proof:
By construction, has a vertex for each cell a knight could occupy and an edge for each pair of cells that attack each other, since a knight on attacks exactly the surviving cells offset by or .
Let be a maximum independent set of , and the maximum number of mutually non-attacking knights. I claim :
- : no two vertices in are adjacent, so knights placed on the cells of do not attack each other, and is the maximum over all such placements.
- : take a maximum placement of non-attacking knights. By construction of , no edges join any two cells of , so is an independent set of size , and the maximum independent set is at least that large.