Introduction

You have applicants and jobs, and lines connecting each applicant to the jobs they're qualified for. You want to hire as many people as possible — pair each to a job they can do, with nobody double-booked. The same shape appears everywhere: students to projects, riders to drivers, donors to patients.

The tempting approach is greedy: walk through the connections and grab any pair that still fits. It's quick — but it can paint itself into a corner. An early, hasty pairing can block two other pairings that would have worked, leaving people unmatched who didn't have to be.

That's the maximum matching problem: find the largest set of pairs. And here's the cheerful news, fitting for a finale: it's easy. Unlike the NP-hard problems across this site, maximum matching is solvable exactly, in polynomial time — and it's the engine inside assignment and kidney exchange.

Make the Pairs

Try it. On the left are applicants, on the right are jobs, and the lines show who can do what. Click a line to pair them (each person can be in only one pair). Make as many pairs as you can.

<p class="hint">{{hint}}</p>
<svg id="svg" viewBox="0 0 300 220" class="svg"></svg>
<div class="meter">{{pairs_made}} <b id="count" class="g">0</b><span id="status" class="status"></span></div>
<div class="btns">
  <button id="greedy" type="button">{{btn_greedy}}</button>
  <button id="max" type="button">{{btn_max}}</button>
  <button id="reset" type="button" class="ghost">{{btn_clear}}</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.hint .g { color: #0a7d33; font-weight: 700; }
.svg { width: 100%; max-width: 380px; background: #f4f7f9; border: 1px solid #e2e6eb; border-radius: 10px; display: block; }
.edge { stroke: #c2ccd6; stroke-width: 3; cursor: pointer; }
.edge:hover { stroke: #8aa0b3; }
.edge.on { stroke: #2a9d8f; stroke-width: 5; }
.node { fill: #457b9d; }
.node.matched { fill: #2a9d8f; }
.nlbl { fill: #fff; font: 800 13px ui-monospace, monospace; text-anchor: middle; dominant-baseline: central; pointer-events: none; }
.meter { margin: .8rem 0 .6rem; font-size: 1.05rem; }
.g { color: #0a7d33; font-family: ui-monospace, monospace; }
.status { margin-left: .6rem; font: 700 .9rem system-ui; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .5rem 1rem; border: 1px solid #457b9d; background: #457b9d; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #457b9d; }
// Code not found

Then compare. Greedy grabs pairs in order and gets stuck at fewer than the best. Maximum uses augmenting paths — it can rearrange existing pairs to free everyone up — and finds the true largest matching. Watch greedy settle for less while the optimal method squeezes in one more pair.

The Good News

Maximum matching is one of the prettiest solved problems in all of algorithms:

  • Checking is easy. Confirm no node is in two pairs and count the pairs.
  • Greedy is not optimal. As the demo shows, a hasty greedy can miss pairs — so we need something smarter.
  • Augmenting paths are the key idea. An augmenting path alternates between unmatched and matched edges and starts and ends at free nodes. Flip its edges and you gain exactly one pair. Keep finding them until none remain, and you've reached the maximum — that's Berge's theorem.
  • It's polynomial. Hopcroft–Karp matches bipartite graphs in O(EV)O(E \cdot \sqrt{V}). For general (non-bipartite) graphs, odd cycles cause trouble, but Edmonds' Blossom algorithm (1965) handles them — a landmark result and an early definition of "efficient."
  • Weighted, too. Add costs and you get minimum-cost matching — the assignment problem, solved by the Hungarian algorithm — and a special case of min-cost flow.

So matching sits squarely in the friendly, conquered part of the complexity map — exact, fast, and the foundation for several other problems on this site.

Where It Matters

Pairing things up optimally is everywhere people and resources meet:

  • Markets: matching job applicants to positions, students to schools, users to recommendations.
  • Ride-hailing and dispatch: pairing riders with the right drivers in real time.
  • Kidney exchange: at its heart, organ matching is a matching problem (with extra constraints).
  • Scheduling: assigning tasks to time slots or workers to shifts.
  • Computer vision: matching features between images is a matching problem.

Because it's exact and fast, maximum matching is a quiet building block inside countless systems — often as the polynomial core that bigger, harder problems are built on.

Conclusion

Maximum matching is a fitting place to end. After cliffs of NP-hardness and ceilings of undecidability, here's a problem that's deep, useful, and solved — beautifully. The trick isn't brute force; it's the augmenting path, the elegant idea that you can always do better by rearranging what you have until no improvement remains.

It also ties the whole collection together: matching is the polynomial heart beating inside assignment, kidney exchange and min-cost flow. Some problems on this site fight you to the bitter end; this one simply yields — and in yielding, quietly pairs the world: applicants to jobs, riders to drivers, donors to patients, as well as the connections will allow.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/max-matching/Content licensed under CC BY-NC 4.0.