Max Flow and Ford-Fulkerson
What This Concept Is
A flow network is a directed graph G = (V, E) with nonnegative edge capacities c(u, v) >= 0, a distinguished source s, and a distinguished sink t. A flow f is an assignment of real numbers to edges satisfying:
- Capacity.
0 <= f(u, v) <= c(u, v)for every edge. - Conservation. For every
v != s, t,sum over u f(u, v) = sum over w f(v, w)- what flows in equals what flows out.
The value of the flow is the net flow leaving s:
|f| = sum over v f(s, v) - sum over v f(v, s)
The max-flow problem asks for a flow of maximum value.
The Ford-Fulkerson method is the template:
f := 0 on every edge
while there exists an augmenting path P from s to t in the residual graph G_f:
delta := min residual capacity along P
push delta units of flow along P
return f
The residual graph G_f has, for each edge (u, v) with f(u, v) < c(u, v), a forward residual edge of capacity c(u, v) - f(u, v), and for each edge with f(u, v) > 0, a backward residual edge of capacity f(u, v). Backward edges allow the algorithm to "undo" a bad decision.
Ford-Fulkerson is a method rather than an algorithm because the choice of augmenting path is unspecified. Different choices yield different concrete algorithms (Edmonds-Karp, Dinic, capacity scaling) with different complexity bounds.
Why It Matters Here
Max flow unifies a surprisingly large family of problems under one algorithmic umbrella:
- bipartite matching (concept 20)
- project / task selection with prerequisite structure (min-cut formulations)
- edge-disjoint and vertex-disjoint paths (Menger's theorem)
- image segmentation with data and smoothness terms (graph cuts in vision)
- baseball elimination (Hoffman 1974)
- survey design, scheduling, and routing with capacities
- the LP dual of min-cut - gateway to linear programming (covered in Module 5)
If you can build the right flow network, you inherit decades of algorithmic progress.
Concrete Example
Network:
Ford-Fulkerson might first find s -> a -> t with bottleneck 4: push 4. Residual now has c(a, t) = 0, backward t -> a with 4.
| step | augmenting path | bottleneck | flow after |
|---|---|---|---|
| 1 | s-a-t | 4 | 4 |
| 2 | s-b-c-t | 9 | 13 |
| 3 | s-a-c-t | min(6, 8, 1) = 1 | 14 |
| 4 | s-b-? | none (b-c saturated, no forward path) | terminate |
Max flow = 14. The min cut {s, a, b, c} vs {t} has capacity c(a,t) + c(c,t) = 4 + 10 = 14, confirming the max-flow/min-cut equality.
Common Confusion / Misconceptions
- "Backward edges look like edges that weren't in the graph." They are residual edges; they encode the ability to cancel previously pushed flow. Without them, a greedy choice on an early augmenting path can permanently block the optimal routing.
- "Ford-Fulkerson is polynomial." No: with adversarial augmenting path choices, it can take
O(|f*|)iterations where|f*|is the max-flow value - exponential in input size if capacities are large, and non-terminating for irrational capacities. - "Only integer capacities make sense." Rational capacities work (scale to integers). Irrational capacities can break termination of the method; this is why Edmonds-Karp or capacity scaling is preferred in theory.
- "Max flow and min cost flow are the same problem." No - min cost max flow adds per-edge costs and asks for the cheapest max flow; solved via successive shortest paths or cycle-cancelling, not plain Ford-Fulkerson.
- "Multiple sources/sinks require a new algorithm." Add a super-source connecting to all real sources with infinite capacity; same for sinks.
How To Use It
Implementation skeleton with explicit residuals:
for each edge (u, v, c):
add forward (u, v, c) and backward (v, u, 0),
cross-link them as reverse pointers
while BFS/DFS finds a path P from s to t using residual edges with capacity > 0:
delta := min residual capacity along P
for each edge e on P:
e.cap := e.cap - delta
e.reverse.cap := e.reverse.cap + delta
total := total + delta
return total
Integration checklist:
- represent every edge with a pointer/index to its reverse so pushing flow updates both sides in
O(1) - treat undirected edges as two directed edges with capacity
ceach (not one withcand one with0) - add super-source and super-sink for multi-source/multi-sink problems
- model vertex capacities by splitting each vertex
vintov_inandv_outwith capacity equal to the vertex constraint - always pick BFS or some structured rule for augmenting paths in practice - this is Edmonds-Karp and guarantees polynomial time
Transfer / Where This Shows Up Later
- S3 (systems). Bandwidth allocation in data center networks, CDN cache placement, container scheduling with resource caps.
- S4 (compilers). Register allocation via min-cut formulations, certain optimization problems on program-dependence graphs.
- S5 (OS). I/O scheduling with device throughput constraints, network QoS models.
- S6 (databases). Query optimization via flow-based cost models, transaction scheduling with conflict flows, partitioning for joint storage.
- S7 (DDD). Capacity planning across bounded contexts, identifying bottleneck integration points via min-cuts of dependency graphs.
- S8 (distributed). Bandwidth routing, multi-commodity flow approximations for traffic engineering, replica placement under capacity constraints.
Back-link to concept 09 (shortest-path variants) because BFS-based augmenting paths reuse the same infrastructure, and forward to concepts 18, 19, 20 for faster variants and applications.
Check Yourself
- Why are backward residual edges necessary, and what role do they play in "undoing" decisions?
- Why is naive Ford-Fulkerson not polynomial in
|V|and|E|alone? - How does the algorithm terminate on integer capacities?
- What is the value of the flow equal to at termination, and why (hint: max-flow min-cut)?
- Transform a multi-source, multi-sink problem into a single-source, single-sink one; what invariant must your super-edges satisfy?
- Give a vertex-capacity example and show the vertex-splitting transformation.
Mini Drill or Application
On the network
s -(3)-> a, s -(2)-> b
a -(1)-> b, a -(3)-> c
b -(2)-> c, b -(3)-> t
c -(2)-> t
run Ford-Fulkerson by hand. Find at least two distinct execution paths (different augmenting-path sequences) that lead to the same max-flow value. Verify the max-flow value by identifying a matching min-cut.
Implementation drills.
- Implement Ford-Fulkerson with explicit residual edges using an adjacency list of
Edgerecords withrevindices. - Build a small test suite: single edge, chain, diamond, and a 10-vertex random network; compare your output against a reference (NetworkX
maximum_flow). - Transform a bipartite matching instance into a max-flow instance and solve it.
- Model a project selection problem (each project has a profit; each prerequisite costs
c_i) as max-flow / min-cut. - Try two pathological instances where DFS-based augmentation explodes in iterations - see why Edmonds-Karp is the default in practice.
Read This Only If Stuck
- CLRS 24.1: Flow Networks
- CLRS 24.1: Flow Networks (Part 2)
- CLRS 24.2: Ford-Fulkerson Method
- ADM 6.5: Network Flows and Bipartite Matching
- Sedgewick: Network Flow
- Sedgewick: Flow Networks
- Competitive Programming: Network Flow
- Competitive Programming: Max Flow Applications
- cp-algorithms: Ford-Fulkerson and Edmonds-Karp
- Stanford CS261: Max Flow