Logo
Published on

Network Flows - Part 4

Authors
  • avatar
    Name
    Till Heller
    Twitter

In Part 3, we proved the Max-Flow Min-Cut Theorem and saw that augmenting paths are the key to finding maximum flows. The natural algorithm is clear: start with the zero flow, find an augmenting path in the residual graph, push flow along it, and repeat. But several important questions remain. Does this process always terminate? How do we choose which augmenting path to use? And how fast is the resulting algorithm? In this part, we'll answer all three questions.

The Ford-Fulkerson Method

Let's start by writing down the augmenting path strategy as a precise algorithm. The Ford-Fulkerson method, introduced by Lester Ford and Delbert Fulkerson in 1956, is the framework that makes this idea concrete.

Ford-Fulkerson Method:

  1. Initialize f(a)=0f(a) = 0 for every arc aAa \in A.
  2. Build the residual graph DfD_f.
  3. Search for an augmenting path PP from ss to tt in DfD_f.
  4. If no augmenting path exists, stop — ff is a maximum flow.
  5. Otherwise, compute the bottleneck Δ(P)=min(u,v)Pcf(u,v)\Delta(P) = \min_{(u,v) \in P} c_f(u,v).
  6. Update the flow: for each arc (u,v)(u,v) on PP, if (u,v)(u,v) is a forward arc, set f(u,v)f(u,v)+Δ(P)f(u,v) \leftarrow f(u,v) + \Delta(P); if it corresponds to a backward arc (v,u)(v,u), set f(v,u)f(v,u)Δ(P)f(v,u) \leftarrow f(v,u) - \Delta(P).
  7. Go to step 2.

We call this a "method" rather than an "algorithm" because it doesn't specify how to find the augmenting path in step 3. Any search procedure — depth-first search, breadth-first search, or something else entirely — can be used. As we'll see, this choice has a dramatic impact on performance.

Correctness

The correctness of the Ford-Fulkerson method follows directly from the Max-Flow Min-Cut Theorem (Theorem 1 from Part 3). When the method terminates, no augmenting path exists in the residual graph. By the equivalence of conditions (i) and (ii) in the theorem, the current flow is maximum.

We should also verify that each augmentation produces a valid flow. This is straightforward: the capacity constraints are preserved because we only push Δ(P)\Delta(P) units, which is at most the residual capacity on every arc. Flow conservation is preserved because on each intermediate vertex of the path, the incoming flow and outgoing flow both change by the same amount Δ(P)\Delta(P).

Termination

Correctness tells us that if the method stops, it gives the right answer. But does it always stop?

For integer capacities, the answer is yes. Each augmentation increases the flow value by at least 11 (since the bottleneck is a positive integer). The flow value is bounded above by any cut capacity, which is finite. So the method must terminate after at most f|f^*| augmentations, where f|f^*| is the maximum flow value.

Observation 2. With integer capacities, the Ford-Fulkerson method terminates after at most f|f^*| augmentations.

This gives a running time of O(mf)O(m \cdot |f^*|), since each augmentation requires finding a path in the residual graph (which takes O(m)O(m) time using BFS or DFS, where m=Am = |A|). For networks with small flow values, this is perfectly fine. But the flow value f|f^*| can be enormous — it can be exponential in the size of the network.

Consider the following example. Take a network with vertices {s,a,b,t}\{s, a, b, t\} and arcs (s,a)(s,a), (s,b)(s,b), (a,b)(a,b), (a,t)(a,t), (b,t)(b,t) with capacities c(s,a)=c(s,b)=c(a,t)=c(b,t)=Cc(s,a) = c(s,b) = c(a,t) = c(b,t) = C and c(a,b)=1c(a,b) = 1, where CC is a large integer. The maximum flow is 2C2C. A clever adversary could force the algorithm to alternately find augmenting paths sabts \to a \to b \to t and sbats \to b \to a \to t, each pushing only 11 unit through the bottleneck arc (a,b)(a,b) or its reverse. This would require 2C2C augmentations — even though a smarter choice of paths would solve the problem in just 22 augmentations1.

ford-fulkerson-bad

For irrational capacities, the situation is even worse: there exist examples where the Ford-Fulkerson method does not terminate at all. The flow values can converge to a limit that is not the maximum flow. This was shown by Uri Zwick in 1993 and is one of the reasons why the Ford-Fulkerson method is considered a framework rather than a complete algorithm — it needs a good path-selection rule to guarantee termination and efficiency.

The Edmonds-Karp Algorithm

The weakness of the Ford-Fulkerson method is its freedom in choosing augmenting paths. Jack Edmonds and Richard Karp (and independently Yefim Dinitz) discovered in 1972 that a simple rule eliminates all the problems: always choose a shortest augmenting path, where "shortest" means fewest arcs.

Definition 10. The Edmonds-Karp algorithm is the Ford-Fulkerson method where the augmenting path in each iteration is a shortest ss-tt-path in the residual graph DfD_f, found using breadth-first search (BFS).

BFS explores vertices layer by layer: first all vertices at distance 11 from ss, then distance 22, and so on. It naturally finds a shortest path in O(m)O(m) time. By always augmenting along a shortest path, we ensure that the residual graph doesn't develop "shortcuts" that could lead to the pathological behavior we saw above.

Analysis

The key to analyzing the Edmonds-Karp algorithm is the following lemma, which shows that distances in the residual graph can only increase over time.

For a flow ff, let df(v)d_f(v) denote the shortest-path distance (in number of arcs) from ss to vv in the residual graph DfD_f.

Lemma 2. Let ff be a flow, and let ff' be the flow obtained after one augmentation along a shortest path. Then df(v)df(v)d_{f'}(v) \geq d_f(v) for every vertex vVv \in V.

The proof uses a careful argument by contradiction: if some vertex got closer to ss after the augmentation, we can trace back the new shorter path and show that it must use an arc that was created by the augmentation — but this leads to a contradiction with the fact that we augmented along a shortest path. We omit the full proof here, but the intuition is that augmenting along shortest paths is "conservative" — it doesn't create shortcuts in the residual graph.

With this lemma, we can bound the total number of augmentations.

Theorem 3. The Edmonds-Karp algorithm performs at most O(nm)O(nm) augmentations, where n=Vn = |V| and m=Am = |A|.

Proof sketch. We call an arc (u,v)(u,v) on an augmenting path critical if it has the minimum residual capacity — that is, cf(u,v)=Δ(P)c_f(u,v) = \Delta(P). After augmenting, a critical arc disappears from the residual graph (its residual capacity drops to zero).

Consider a specific arc (u,v)(u,v). When it is critical, we have df(v)=df(u)+1d_f(v) = d_f(u) + 1 (since it lies on a shortest path). For (u,v)(u,v) to reappear in the residual graph later, flow must be pushed backwards through it — meaning (v,u)(v,u) must appear on some future augmenting path. At that point, df(u)=df(v)+1d_{f''}(u) = d_{f''}(v) + 1. By Lemma 2, distances never decrease, so: df(u)=df(v)+1df(v)+1=df(u)+2.d_{f''}(u) = d_{f''}(v) + 1 \geq d_f(v) + 1 = d_f(u) + 2.

Each time an arc becomes critical, the distance of one of its endpoints from ss increases by at least 22. Since distances are bounded by nn (there are only nn vertices), each arc can be critical at most O(n)O(n) times. There are O(m)O(m) arcs, so the total number of critical arc events is O(nm)O(nm). Since each augmentation has at least one critical arc, the total number of augmentations is at most O(nm)O(nm).

Since each augmentation takes O(m)O(m) time (for BFS plus the flow update), the total running time is:

Corollary 2. The Edmonds-Karp algorithm runs in O(nm2)O(nm^2) time.

This is a polynomial bound that depends only on the size of the network, not on the magnitude of the capacities. Whether the capacities are 11 or 1,000,0001{,}000{,}000, the algorithm runs in the same polynomial time.

A Worked Example

Let's trace the Edmonds-Karp algorithm on a slightly larger network to see it in action. Consider the network with vertices {s,a,b,c,t}\{s, a, b, c, t\} and the following arcs and capacities:

c(s,a)=10,c(s,b)=8,c(a,b)=5,c(a,c)=7,c(b,c)=10,c(b,t)=7,c(c,t)=10.c(s,a) = 10, \quad c(s,b) = 8, \quad c(a,b) = 5, \quad c(a,c) = 7, \quad c(b,c) = 10, \quad c(b,t) = 7, \quad c(c,t) = 10.

Iteration 1. Starting from the zero flow, the residual graph equals the original network. BFS from ss finds a shortest path. One such path is sacts \to a \to c \to t with bottleneck min(10,7,10)=7\min(10, 7, 10) = 7. We push 77 units: f(s,a)=7f(s,a) = 7, f(a,c)=7f(a,c) = 7, f(c,t)=7f(c,t) = 7. Flow value: 77.

Iteration 2. BFS finds sbts \to b \to t with bottleneck min(8,7)=7\min(8, 7) = 7. We push 77 units: f(s,b)=7f(s,b) = 7, f(b,t)=7f(b,t) = 7. Flow value: 1414.

Iteration 3. BFS finds sabcts \to a \to b \to c \to t with bottleneck min(3,5,10,3)=3\min(3, 5, 10, 3) = 3. We push 33 units: f(s,a)=10f(s,a) = 10, f(a,b)=3f(a,b) = 3, f(b,c)=3f(b,c) = 3, f(c,t)=10f(c,t) = 10. Flow value: 1717.

Iteration 4. BFS finds sbcts \to b \to c \to t. The residual capacities are cf(s,b)=1c_f(s,b) = 1, cf(b,c)=7c_f(b,c) = 7, cf(c,t)=0c_f(c,t) = 0 — but cf(c,t)=0c_f(c,t) = 0, so this path is blocked. In fact, no path from ss to tt exists in the residual graph: the arcs (c,t)(c,t) and (a,c)(a,c) are fully saturated, and the remaining forward capacity out of ss is only cf(s,b)=1c_f(s,b) = 1, but it cannot reach tt2.

Wait — let's be more careful. Let me recheck. From ss: we can reach bb (residual 11 on (s,b)(s,b)) and aa via (a,s)(a,s)? No, (a,s)(a,s) is a backward arc with residual 1010, but that goes from aa to ss — it doesn't help us leave ss. Actually, ss can go to bb with residual 11. From bb: (b,t)(b,t) has residual 00, (b,c)(b,c) has residual 77, and there's a backward arc (b,a)(b,a) with residual 33. From cc: (c,t)(c,t) has residual 00, backward arcs (c,a)(c,a) with residual 77 and (c,b)(c,b) with residual 33. None of these reach tt.

So the algorithm terminates with maximum flow f=17|f^*| = 17. The minimum cut can be read from the residual graph: SS is the set of vertices reachable from ss, which is {s,b,c,a}={s,a,b,c}\{s, b, c, a\} = \{s, a, b, c\} and T={t}T = \{t\}. The cut capacity is c(b,t)+c(c,t)=7+10=17c(b,t) + c(c,t) = 7 + 10 = 17, confirming f=17|f^*| = 17.

The algorithm needed only 33 augmentations — far fewer than the theoretical bound of O(nm)O(nm) — because BFS guided us to efficient choices.

edmonds-karp-steps

Looking Ahead

The Edmonds-Karp algorithm gives us a reliable polynomial-time method for computing maximum flows. While O(nm2)O(nm^2) is efficient enough for many practical purposes, faster algorithms exist — Dinitz's algorithm achieves O(n2m)O(n^2 m) using the concept of blocking flows, and more recent algorithms improve this further.

In the next part, we'll shift from algorithms to applications. We'll see how maximum flow can be used to solve problems that, at first glance, have nothing to do with flows: bipartite matching, edge-disjoint paths, and project selection. These applications demonstrate the remarkable versatility of network flow theory.

Footnotes

  1. This example shows that the choice of augmenting path matters enormously. With DFS, a poor choice of paths can lead to O(C)O(C) augmentations, while BFS would find paths that avoid the bottleneck arc entirely.

  2. To verify this, note that ss can only reach bb via (s,b)(s,b) with capacity 11. From bb, the arc (b,t)(b,t) has cf(b,t)=0c_f(b,t) = 0 and (b,c)(b,c) has cf(b,c)=7c_f(b,c) = 7, but from cc, the arc (c,t)(c,t) has cf(c,t)=0c_f(c,t) = 0. Going backwards from cc to aa (via reverse of (a,c)(a,c)) reaches aa, but aa has cf(a,b)=2c_f(a,b) = 2 leading back to bb — a dead end. So tt is unreachable.