Flows & Matching
The polynomial half of the hard problems. Coloring & Covering ended on a cliff - independent set, clique, vertex cover, all NP-hard on a general graph. This page is the other side: matching and minimum cut are both polynomial, both reduce to the same algorithm, and between them they make a surprising number of "assign these to those" and "what is the bottleneck" problems tractable.
The practical value is almost entirely in recognising the reduction. Writing max-flow from scratch in an interview is rare; noticing that a scheduling or assignment problem is max-flow is the skill.
1. Flow networks and max flow
A flow network is a directed graph where every edge has a capacity,
plus two distinguished vertices: a source s and a sink t. A
flow assigns each edge an amount up to its capacity, subject to one rule -
conservation: for every vertex other than s and t, what flows in must
flow out. The value of a flow is the total leaving s. Max flow asks for
the largest possible value.
Capacities are pipe diameters, the flow is how much water actually moves, and conservation says no vertex is a tank - water neither accumulates nor appears. Max flow is the most water the network can carry from the tap to the drain, and it is limited by the narrowest set of pipes you would have to cut to disconnect them - not by any single pipe.
The augmenting-path idea
Every max-flow algorithm is the same loop: find a path from s to t with
spare capacity, push as much as that path allows, repeat until no such path
exists. The one non-obvious ingredient is the residual edge: when you push
f units along u -> v, you also create (or increase) a reverse edge
v -> u with capacity f. Pushing flow back along it means "undo part of an
earlier decision," and without that escape hatch the greedy gets stuck at a
suboptimal answer.
capacity[v][u] += push fails silentlyOmit capacity[v][u] += push and the algorithm returns a wrong answer
without ever failing. The residual edge is what makes the greedy provably
optimal - it is the only way a later augmenting path can route flow around
an earlier bad choice. On the classic 4-vertex example (s->a, s->b,
a->b, a->t, b->t, all capacity 1 except a->b) the greedy first picks
s->a->b->t, and only a residual push back along b->a lets it reach the
true answer of 2.
Finding augmenting paths with BFS (shortest first) is Edmonds-Karp,
O(V * E^2); with DFS it is Ford-Fulkerson and may not even terminate on
irrational capacities. Use BFS. The shortest-augmenting-path rule is what
turns "repeat until stuck" into a polynomial bound.
2. Max-flow min-cut
An s-t cut partitions the vertices into two sets with s on one side
and t on the other; its capacity is the total capacity of the edges
crossing from the s side to the t side. The max-flow min-cut theorem
says these two numbers are always equal:
The maximum flow from
stotequals the minimum capacity of ans-tcut.
One direction is obvious: every unit of flow must cross every cut, so flow
can never exceed any cut's capacity. The other direction is what the algorithm
proves: when no augmenting path remains, the set of vertices still reachable
from s in the residual graph is one side of a cut whose every crossing edge
is saturated - so that cut's capacity equals the flow you found.
Max flow is the answer; min cut is the reason for the answer. They are
the same number seen from opposite sides - one says "this much can get
through," the other says "because these edges are the bottleneck." So an
algorithm that computes the flow also hands you the bottleneck for free: run
one last BFS from s in the residual graph, and the crossing edges are the min
cut.
Extracting the cut needs the original capacities kept alongside the
residual ones. max_flow mutates capacity in place, so by the time it
finishes you can no longer tell "this edge was saturated" from "this edge never
existed" - both read as 0. Deep-copy the capacity matrix before running the
flow, or the cut you extract will be missing exactly the edges that matter.
Minimum cut is polynomial; maximum cut is NP-hard. These read as a
symmetric pair and are nothing alike - the duality with flow exists only for
the minimum. Similarly, min cut between a specific s and t is what flow
gives you; "global min cut" over all pairs is a different (still polynomial)
problem needing a different algorithm.
3. Bipartite matching
A matching is a set of edges no two of which share a vertex. On a bipartite graph, maximum matching is the single most reusable flow reduction there is, because it is what every "assign each of these to one of those" problem actually is.
You rarely need the full flow machinery. The direct algorithm is a DFS that tries to find an augmenting path: an unmatched left vertex looks for a right vertex that is either free, or matched to someone who can be bumped elsewhere.
seen set must be fresh per left vertexThe seen set must be fresh per left vertex, and must live on the right
side. Hoisting it outside the for u loop makes each right vertex
considered at most once across the whole run, which silently under-counts the
matching. Putting it on the left side instead lets the recursion revisit a
right vertex and loop. One set() per try_assign call from the top, keyed on
right vertices - that is the contract, and both deviations produce answers
that look plausible on small inputs.
"Can you move over one seat?" An augmenting path is exactly that request propagating down a chain: the newcomer asks a seated person to shift, who asks the next, until someone finds an empty seat. If the chain reaches an empty seat, everyone shifts and the matching grows by one; if it dead-ends, this newcomer genuinely cannot be seated.
The duality theorems
Bipartite graphs are where the NP-hard problems from Coloring & Covering collapse into polynomial ones, and these two theorems are why:
| Theorem | Statement | What it buys |
|---|---|---|
| Konig's theorem | in a bipartite graph, max matching = min vertex cover | Minimum vertex cover - NP-hard in general - becomes a matching computation. With the complement identity from Coloring & Covering, maximum independent set comes free too: n - max matching. |
| Hall's marriage theorem | a perfect matching saturating the left side exists iff every subset S of the left has |neighbours(S)| >= |S| | A certificate of impossibility: when no perfect matching exists, there is a specific left subset with too few neighbours to blame, which is what "why is this schedule infeasible" questions want. |
| Dilworth / min path cover | in a DAG, the minimum number of paths covering every vertex = n - max matching in a derived bipartite graph | Turns "fewest chains/servers/pipelines to cover all tasks" into matching. The derived graph splits each vertex into an out-copy and an in-copy. |
These theorems are bipartite-only, and the general-graph versions are false. Maximum matching on a general graph is still polynomial (Edmonds' blossom algorithm), but max matching no longer equals min vertex cover, and Hall's condition no longer characterises anything. If a problem's graph is not bipartite, check that first - it is the load-bearing hypothesis, and it is also usually the thing the problem is quietly telling you by splitting the input into two kinds of thing.
4. Recognising the reduction
This is the part worth memorising. Flow problems in the wild almost never say "flow."
| Problem shape | The reduction |
|---|---|
| "Assign each worker to at most one task" | Bipartite matching. Workers left, tasks right, an edge per eligible pair. |
"Each worker can take up to k tasks" | Same, but the source-to-worker edge gets capacity k instead of 1. This is why the flow formulation is worth knowing even when plain matching would do - capacities generalise where matching does not. |
| "Minimum number of X covering every Y" | Minimum vertex cover, so on a bipartite graph: max matching (Konig). |
| "Maximum set with no two conflicting" | Maximum independent set, so on a bipartite graph: n - max matching. |
| "Fewest paths/chains to cover all tasks" | Minimum path cover on a DAG: n - max matching on the split graph. |
| "What is the bottleneck / cheapest set of links to sever" | Minimum cut, so max flow. |
| "Choose a subset to maximise profit minus penalties" | Project selection / max-closure, solved as a min cut with infinite-capacity dependency edges. |
| Grid problems: "place non-attacking pieces", "cover with dominoes" | Colour the grid like a chessboard - it is bipartite by construction, so matching applies. Domino tiling is perfect matching. |
Two sides plus "at most one each" means matching; capacities or "at most k
each" means flow; "cheapest thing to break" means min cut. Those three tells
cover nearly every flow problem that shows up outside a competitive-programming
contest.
Where to go next
- Coloring & Covering - the NP-hard problems that Konig's theorem rescues on bipartite graphs.
- Shortest Paths & Bipartite Check - how to verify the bipartite hypothesis these reductions all depend on.