DSA DSA In Depth
0%
lesson max-flow-min-cutdualityproofs

The Max-Flow Min-Cut Theorem: Three-Way Equivalence Proof & Integrality Theorem

7 min read Module 5.3: Network Flow, Min-Cut & Bipartite Matching Updated Oct 23, 2026
📄 Cheat Sheet
Network Flows: Max-Flow Min-Cut Theorem & Ford-Fulkerson Algorithm
Back To Back SWE Network Flows: Max-Flow Min-Cut Theorem & Ford-Fulkerson Algorithm Watch on YouTube ↗

Rigorous derivation of weak duality, the three-way Max-Flow Min-Cut equivalence, and the Integrality Theorem.

0:00 / --:--

Flow Across a Cut, Weak Duality & The Three-Way Equivalence

Let $(S, T)$ be any partition of $V$ with $s \in S$ and $t \in T$. The capacity of the cut $c(S, T)$ counts only directed edges going forward from $S$ to $T$:

$$c(S, T) = \sum_{u \in S} \sum_{v \in T} c(u, v)$$

By summing the flow conservation equation over all $u \in S$, internal edges $(x, y) \in S \times S$ cancel out in pairs, yielding the Net Flow Across a Cut Lemma:

$$|f| = f(S, T) - f(T, S) = \sum_{u \in S, v \in T} f(u, v) - \sum_{v \in T, u \in S} f(v, u) \le \sum_{u \in S, v \in T} c(u, v) = c(S, T)$$
Formal Theorem — The Max-Flow Min-Cut Theorem (Ford & Fulkerson 1956, Elias-Feinstein-Shannon 1956)

For any flow $f$ in network $G = (V, E, c, s, t)$, the following three statements are logically equivalent:

  1. (i) Optimality: $f$ is a maximum flow in $G$.
  2. (ii) Termination: The residual network $G_f$ contains no augmenting path from $s$ to $t$.
  3. (iii) Strong Duality Certificate: $|f| = c(S, T)$ for some $s$-$t$ cut $(S, T)$ of $G$ (which is therefore a minimum cut).

Proof of (ii) $\implies$ (iii): Suppose $G_f$ has no $s \leadsto t$ path. Define $S = \{v \in V : s \text{ can reach } v \text{ in } G_f\}$ and $T = V \setminus S$. Clearly $s \in S$ and $t \in T$. For any pair $(u, v)$ with $u \in S, v \in T$:
• If $(u, v) \in E$, then $c_f(u, v) = c(u, v) - f(u, v) = 0$ (otherwise $v$ would be reachable from $u \in S$), so $f(u, v) = c(u, v)$ (every forward cut edge is saturated).
• If $(v, u) \in E$, then $c_f(u, v) = f(v, u) = 0$ (otherwise $v$ would be reachable via the backward residual edge), so no flow returns from $T$ to $S$.
Substituting both into the Net Flow Lemma gives $|f| = f(S, T) - f(T, S) = c(S, T) - 0 = c(S, T)$. $\blacksquare$

flowchart LR Stmt1["(i) f is a Maximum Flow"] -->|"Contrapositive: augmenting path increases |f|"| Stmt2["(ii) G_f has no s ~> t augmenting path"] Stmt2 -->|"Let S = reachable from s in G_f"| Stmt3["(iii) |f| = c(S, T) for cut (S, V \ S)"] Stmt3 -->|"Weak Duality: |f'| <= c(S, T) for all f'"| Stmt1
Dinic’s Max-Flow: BFS Level Graph, Blocking Flow, S-T Min-Cut & Hopcroft-Karp
O(V²E) Dinic · O(E√V) Unit / Matching

Visualize Dinic’s BFS level graph L_G and current-arc DFS blocking flows alongside backward residual arcs (v→u), extract the saturated S-T minimum cut, or step through Hopcroft-Karp disjoint shortest augmenting paths.

Step 1 / 8
Custom Input:
Timeline

Invariant & State

Phase 1 Blocking Flow: Pushed 4 along S→A→C→T and 10 along S→{A,B}→D→T. Edges S→A, A→C, D→T saturated! Total flow = 14.
10/10 4/10 4/4 6/8 4/9 4/10 0/6 10/10 S lvl=0 A lvl=1 B lvl=1 C lvl=2 D lvl=2 T lvl=3
Total Max-Flow |f|
14
Dinic Phase
Phase 1 Blocking Flow (level[T] = 3)
S-T Min-Cut Capacity
c(S→A)=10 + c(B→D)=9 = 19
[Step 1] Phase 1 Blocking Flow: Pushed 4 along S→A→C→T and 10 along S→{A,B}→D→T. Edges S→A, A→C, D→T saturated! Total flow = 14.

The Integrality Theorem

Because Ford-Fulkerson initializes $f = 0$ and each iteration computes $\Delta = \min_{e \in P} c_f(e)$ via subtraction and minimum operations, if all capacities $c(e) \in \mathbb{Z}_{\ge 0}$ are integers, then $\Delta$ is a positive integer at every step. This yields the Integrality Theorem: every network with integer capacities admits an integer maximum flow $f^*(e) \in \mathbb{Z}$. This single fact allows us to solve discrete combinatorial matching and assignment problems using continuous network flow!

Engineering Nuance — Saturated Edges Are Not Necessarily Min-Cut Edges

A common interview mistake is extracting the minimum cut by collecting all edges with $f(u, v) == c(u, v)$. Many internal edges inside $S \times S$ or $T \times T$ may also be $100\%$ saturated without lying on the minimum cut! The canonical extraction runs a final BFS from $s$ over edges with $c_f > 0$ to mark inS[u], and then outputs all original directed edges $(u, v) \in E$ where inS[u] && !inS[v].

OperationAlgorithmic ProcedureTime ComplexitySpace Complexity
Verify Flow OptimalityBFS from $s$ in $G_f$ ($c_f > 0$); check if $t$ is unvisited$O(V + E)$$O(V)$
Extract Vertex Partition $(S, T)$Collect S = {u : vis[u]} after residual BFS from $s$$O(V + E)$$O(V)$
Extract Min-Cut Edge SetScan $e = (u, v) \in E$ with vis[u] && !vis[v]$O(E)$$O(|\text{Cut}|)$

Production C++20 Implementation: Canonical Min-Cut Partition & Edge Extractor

#include <vector>
#include <queue>
#include <utility>

struct ResidualNetwork {
    struct Edge {
        int from, to;
        long long cap, flow;
    };
    int n;
    std::vector<Edge> edges;
    std::vector<std::vector<int>> adj;

    struct MinCutResult {
        std::vector<uint8_t> inSourceSide; // 1 if u in S, 0 if u in T
        std::vector<std::pair<int, int>> cutEdges;
        long long cutCapacity = 0;
    };

    // Call AFTER maxFlow(s, t) has terminated
    MinCutResult extractMinCut(int s) const {
        MinCutResult res;
        res.inSourceSide.assign(n, 0);
        std::queue<int> q;
        q.push(s);
        res.inSourceSide[s] = 1;

        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int id : adj[u]) {
                const auto& e = edges[id];
                long long residual = e.cap - e.flow;
                if (residual > 0 && !res.inSourceSide[e.to]) {
                    res.inSourceSide[e.to] = 1;
                    q.push(e.to);
                }
            }
        }

        // Only inspect original forward edges (even indices 0, 2, 4, ...)
        for (size_t id = 0; id < edges.size(); id += 2) {
            const auto& e = edges[id];
            if (res.inSourceSide[e.from] && !res.inSourceSide[e.to]) {
                res.cutEdges.emplace_back(e.from, e.to);
                res.cutCapacity += e.cap;
            }
        }
        return res;
    }
};
Failure Mode — Including Reverse Residual Edges in $c(S, T)$

Remember that cut capacity $c(S, T) = \sum_{u \in S, v \in T} c(u, v)$ is directional! An original edge $(v, u)$ directed from $T$ into $S$ has capacity $c(v, u)$ that does not contribute to $c(S, T)$ (and at optimality carries $f(v, u) = 0$ flow). Only original edges directed from $S$ to $T$ (even indices in our paired array) belong to the $s$-$t$ cut.

Check yourself Question 1 of 4
Concept Check Directional Capacity of an s-t Cut recall
Section: Cut Capacity Definition

Given an $s$-$t$ cut $(S, T)$ with $s \in S$ and $t \in T$, how is the capacity $c(S, T)$ defined?

⚡ Interactive Challenge · Staff Level

Algorithmic Design & Complexity Check

Analyze the invariant, asymptotic bound, and hardware behavior before selecting.

After running Dinic's algorithm, you want to find all directed edges $e = (u, v) \in E$ such that increasing $c(e)$ by $+1$ strictly increases the maximum $s$-$t$ flow. Which condition characterizes such edges?

Key Takeaways

Weak duality guarantees |f| = f(S, T) - f(T, S) <= c(S, T) for every flow f and every s-t cut (S, T).
When no augmenting path exists in G_f, the set S of vertices reachable from s in G_f defines a minimum cut with |f| = c(S, T).
The Integrality Theorem guarantees that integer edge capacities always produce an integer-valued maximum flow.
To extract min-cut edges, run a residual BFS from s and select original edges (u, v) with u in S and v in T—never simply pick all saturated edges.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue