A greedy algorithm commits to the locally best choice and never undoes it. It’s correct when some optimal solution starts with the greedy choice and the rest is the same problem again. Unproven greedy is the most common source of WA, so the proof technique matters as much as the rule.
Finding the rule
- Brute force small inputs and look at optimal solutions. What’s chosen first? In what order?
- Try sort keys: value, deadline, end time, ratio , difference , sum , or a comparator derived from two elements (below).
- Stress test the candidate rule against a brute force before coding the real thing.
Adjacent exchange: finding the sort key
For “order items to minimise a cost”, look at two adjacent items. Put before iff , and rearrange it into .
Smith’s rule (minimise on one machine): first costs , first costs . So goes first iff .
Proof: Smith's rule, and why adjacent exchange proves a global order
Local step. before costs and before costs ; every other job’s completion time is unaffected by swapping only (they occupy the same two time slots either way, and no job outside the pair moves). Subtracting, (-first cost) (-first cost) . So -first is at most as costly as -first iff , i.e. (dividing by ) iff . Global step. If some optimal order has immediately after with , swapping them keeps the same set of jobs and the same completion times for everyone else, and by the local step does not increase cost — so some optimal order has no adjacent inversion against this key, i.e. is sorted by . Since any order can be turned into the fully sorted one through a sequence of adjacent swaps that each fix one inversion, and each such swap is non-worsening by the local step, the sorted order is itself optimal.
More keys derived the same way:
| Objective | Sort by |
|---|---|
| shortest processing time first | |
| (lateness) | earliest deadline |
| ascending (compare as ) | |
| boxes/people stacking with strength and weight , minimise max load | |
| concatenate numbers into the largest number | as strings |
| tasks with requirement to start and cost : complete as many as possible | descending |
| dragons/monsters where defeating needs strength and gives | first by ascending; then by descending |
| two-machine flow shop | Johnson’s rule (Scheduling) |
Transitivity
The pairwise rule must be a strict weak ordering. Ratios, differences and sums are fine. For an unusual rule, test transitivity on random triples, or prove it by writing the comparison as . When ties matter (equal keys with different effects), add a second key and re-prove.
Exchange for selections
Take an optimal solution and the greedy solution . At the first difference, swap in greedy’s choice; show feasibility is kept and the objective doesn’t get worse. By induction, some optimal solution equals .
A second proof style, potential / bound, needs no exchange: find an a priori lower bound on the objective (an invariant, a counting argument, a matroid rank), then show greedy’s output meets it exactly. This is often shorter when the bound is easy to state (e.g. the number of components merged, a sum over a fixed set of constraints) but the exchange step would be fiddly.
Greedy stays ahead is the same idea measured stepwise: after steps, greedy’s progress is at least as good as any solution’s after steps. Interval scheduling by earliest finish: the -th greedy interval ends no later than the -th of any feasible schedule.
Proof: earliest-finish-first is optimal for interval scheduling
Let be greedy’s choices and any feasible (pairwise non-overlapping) set of intervals, both sorted by finish time. Claim: for every for which both exist. By induction on : for , is the earliest-finishing interval overall, so . For the step, assume ; since starts after finishes, starts after finishes too, so is a candidate greedy could have picked at step (compatible with ); greedy picks the earliest-finishing such candidate, so . This proves the claim. Now suppose : then exists and starts after (by the claim at ), so is compatible with all of — but then greedy would not have stopped at intervals, a contradiction. So for every feasible , i.e. is optimal.
Regret greedy (priority queue with undo)
Take tentatively. When the constraint breaks, throw away the worst item taken so far. The proof is an exchange: the discarded item is never better than what replaced it.
Proof: regret greedy for unit jobs with deadlines maximises profit
Process jobs in deadline order, maintaining a set of currently-accepted jobs with for every prefix processed so far (enforced by discarding the minimum-profit job in whenever a new job would push some deadline’s count too high). Claim: at every point, has the maximum possible total profit among sets of size that are feasible using only the jobs seen so far. By induction on the number of jobs processed: adding a new job with profit either fits (feasible sets of the new size containing plus an optimal old are optimal, since is forced available at this point and nothing before benefits from excluding it) or forces a removal; when forced, removing is correct because any feasible set of the same size using the same job pool has to drop some job to stay within every deadline’s capacity, and dropping the globally smallest profit among all currently-held candidates (old plus ) cannot be beaten by dropping any other single job — any other choice of removal leaves a set with total profit at most the profit of keeping the largest values, which is exactly what discarding the minimum achieves. Formally: among all subsets of of size , the one maximising profit sum is the one dropping the single smallest element, since removing any other element instead of gives a set containing but missing , with profit sum smaller by . Since the deadline constraint only bounds (unit jobs, one slot per time unit up to each deadline), any set respecting all capacity bounds is exactly a set of the right size at every prefix, so maximising the sum at every step maximises it at the end.
// unit jobs with deadlines d and profits p: maximise total profit
sort(all(jobs)); // by deadline
priority_queue<ll, vector<ll>, greater<>> taken; // min-heap of profits
for (auto [d, p] : jobs) {
taken.push(p);
if ((int)taken.size() > d) taken.pop(); // too many jobs before deadline d
}The same shape covers:
- Moore-Hodgson (fewest late jobs): drop the longest job taken so far when the deadline is violated.
- Minimum refuelling stops: when out of fuel, refuel at the best station passed.
- Buy low, sell high with unlimited transactions and one share per day (CF 865D): push each price twice. When the current price beats the cheapest in the heap, take the profit and push the current price so the trade can later be “extended”.
- Choosing non-adjacent elements with maximum sum: take the maximum , replace it and its neighbours by (a regret node), repeat times with a heap and a linked list. This is a simple instance of the general “simulate min-cost flow augmentations with heaps” technique.
Greedy as simulated flow
Many greedy-with-heap solutions are min-cost flow where every augmenting path is short (one or two edges) and can be found with heaps. If a problem is naturally a flow (matching people to items on a line, buy and sell, assigning tasks with capacities) and is -, look for the greedy that simulates the augmentations, with “regret” entries representing reverse edges.
Huffman coding and optimal merging
Repeatedly merge the two smallest weights. The cost (sum of weights times depth, or the total merge cost of sorted runs) is minimised.
Proof: Huffman merging minimises
Two smallest weights are deepest siblings in some optimal tree. In any full binary tree (every internal node has 2 children — an optimal tree is always full, else a unary node could be removed, strictly improving cost), two of the deepest leaves are siblings. Let have the two globally smallest weights. If the sibling pair at maximum depth is , swap into ‘s position and into ‘s (and likewise for if needed): since and (as was at the deepest level), moving the smaller weight to the deeper position changes the cost by — cost does not increase. Repeating for the second element produces an optimal tree with as deepest siblings. Reduction. In that tree, replace the pair by a single leaf with weight at their shared parent’s position: , where is the tree on the remaining symbols (every other leaf keeps its depth; each lose one level of depth relative to , accounting for the exactly, since ). So minimising over trees with as siblings is equivalent to minimising over trees on the reduced problem, plus the constant . Induction on (base case : the only tree is optimal) gives that repeatedly merging the two smallest weights is optimal at every step, hence overall. Prefix-free. Every symbol sits at a distinct leaf and codes are root-to-leaf paths, so no code is a prefix of another (that would require one leaf to be a proper ancestor of another, impossible in a tree where every symbol is a leaf).
- after sorting: two queues, one of sorted leaves and one of merged nodes (merged sums come out in non-decreasing order).
- -ary Huffman: pad with zero-weight leaves until , then merge the smallest each time.
- If the order of leaves must be kept (alphabetic codes, optimal BST-like merging of adjacent piles), greedy fails: use Hu-Tucker or Garsia-Wachs, or the interval DP with Knuth’s optimisation (Knuth-Yao).
- Adaptive Huffman: update the tree as symbols stream in with no upfront frequency table (FGK/Vitter algorithms).
- Canonical Huffman: reassign codes by (length, symbol order) only, so the code table transmits as just a list of lengths.
- Arithmetic / range coding: a different entropy coder that beats Huffman on skewed distributions by not rounding each code to a whole number of bits; used alongside Huffman-style merging in JPEG, DEFLATE and MP3.
- The same two-smallest-first merge minimises total cost for merging sorted runs in external sorting (optimal merge patterns) — it’s the same proof, with “merge cost” standing in for “weighted depth”.
Classic greedy table
| Problem | Rule |
|---|---|
| max non-overlapping intervals | earliest end |
| min points stabbing all intervals | sort by end, place a point at each uncovered end |
| min rooms | max overlap depth; assign with a min-heap of end times |
| fractional knapsack | value / weight descending |
| assign tasks to workers, maximise matches with | sort both, two pointers |
| minimise | sort both, pair in order |
| maximise | sort both the same way (rearrangement inequality) |
| jump game, min jumps | BFS-like ranges: furthest reach per layer |
| canonical coin systems | largest coin first (check canonicity by brute force up to the sum of the two largest coins) |
| MST | Kruskal / Prim (cut property) |
| matroid maximum weight basis | sorted greedy (Matroids) |
Recognising a matroid is itself a correctness proof — no exchange argument needed, since greedy-by-weight is always optimal on a matroid’s independent sets (Matroids). Common matroids worth spotting on sight: spanning forests (graphic matroid, Kruskal), linearly independent vectors over a field or ( linear/XOR basis), partition matroids (at most items from group ), uniform matroids (any items), and scheduling unit jobs against deadlines (a transversal matroid — this is exactly why the regret-greedy construction above works).
Proof: sorting both arrays the same way minimises
Lemma. For any reals and : . Using the representation , fix any and write , etc.; since and , and as 0/1 values. Checking the (small number of) possible combinations of and directly: whenever or , both sides of the pointwise inequality are equal; the one remaining combination gives left side and right side , so holds. The pointwise inequality holds in every case, so it holds after integrating over , proving the lemma.
Applying it. Suppose (equivalently the sequence paired with , here renamed to match the lemma) is sorted and is not order-preserving on : then there exist with but . Set and (renaming so matches the lemma). The current pairing contributes (the cross pairing); swapping to match with and with contributes instead, with every other term unchanged. The lemma (with as just defined) says exactly , so the swap never increases the total cost. Repeating removes every inversion between ‘s induced order on and the sort order on , reaching the fully sorted pairing without ever increasing the cost — so some optimal pairing is the sorted one.
When greedy fails
| Problem | Why |
|---|---|
| 0/1 knapsack | indivisible items; ratio order isn’t optimal |
| coin change with , target 6 | largest-first gives 3 coins, optimum is 2 |
| weighted interval scheduling | one long valuable interval vs several short ones; DP |
| set cover, vertex cover, TSP | NP-hard; greedy gives only approximations (, 2, ) |
| graph coloring | depends on vertex order (optimal on interval/chordal graphs with the right order) |
An exchange argument that doesn’t close is evidence the greedy is wrong. Look for the counterexample with 3-4 elements.
Practice
CSES: Movie Festival, Tasks and Deadlines, Stick Lengths, Room Allocation, Factory Machines (binary search + greedy). Codeforces: the “greedy” tag, rating 1200–2000, is the densest source of exchange-argument practice.