Grab It Now, or Plan the Whole Thing?
Two strategies, one question. Greedy commits to the locally best move and never reconsiders it. Dynamic programming solves every overlapping subproblem once and remembers the answer. Both can be pointed at the same problem, and they can return different results — so the real skill is not writing either one, it is knowing which one a problem admits. Below, one four-line example settles it: coins {1, 3, 4}, target 6.
Take the best-looking move right now, never backtrack. One cheap decision per step.
Solve every subproblem once, store the answer, build the optimum out of stored answers.
The thing both need: an optimal whole is built from optimal parts.
The extra thing only greedy needs: the local best is inside some global optimum.
Make exactly 6 using coins 1, 3 and 4. Click coins to spend them. Use as few coins as you can — or press Let greedy play to watch the biggest-coin-first rule do it for you.
The four lines that decide it
Coin change is the cleanest place to see the split, because the same problem statement flips between the two techniques depending on nothing but the coin set. You are given denominations and a target, and you want the fewest coins that sum to it exactly. The obvious rule — the one every cashier uses — is to take the biggest coin that still fits, subtract, and repeat. That is a greedy algorithm, and with denominations {1, 3, 4} and a target of 6 it goes:
Three coins, and greedy is finished — it never revisits the decision to grab the 4, because not revisiting is the whole definition. Dynamic programming refuses to grab anything. It works out the cheapest way to make 1, then 2, then 3, all the way up to the target, storing each answer as it goes. When it reaches 6 it tries every coin as the last coin and takes the best of the stored answers underneath:
Two coins. Same problem, same denominations, and greedy is not merely unlucky — it is provably wrong here, and it is wrong for a reason you can point at. Taking the 4 leaves a remainder of 2, and 2 is the single worst amount this coin set can produce: it costs two coins where 3 and 4 each cost one. Greedy paid one coin to walk into the most expensive corner of the problem, and had already thrown away its right to reconsider.
The trap is not the algorithm, it is the coin set. Run the exact same greedy on {1, 5, 10, 25} and it is optimal for every single amount — which is why the rule feels correct: you have been executing it correctly at cash registers your whole life on a denomination system deliberately designed to make it safe. Act 3 of the simulator lets you swap sets and watch the verdict flip.
What both need, and what only greedy needs
Greedy and DP are not opposites. They share a prerequisite and differ by exactly one extra condition, and naming the two precisely is what turns "try greedy and see" into an actual decision.
Optimal substructure (both)
An optimal solution to the whole problem contains optimal solutions to its subproblems. If the cheapest way to make 6 ends in a coin of 3, then the part before that coin must be the cheapest way to make 3 — otherwise you could swap in a cheaper way to make 3 and beat your own optimum, a contradiction.
This is the licence to build big answers out of small ones. Without it, neither technique applies: longest simple path in a graph has no optimal substructure, which is exactly why it has no DP formulation and is NP-hard.
Greedy-choice property (greedy only)
The move that looks best locally is contained in at least one globally optimal solution. Not "is the only optimum" — just "is safe to commit to", so nothing optimal is ruled out by taking it.
Coins {1, 3, 4} with target 6 have optimal substructure but not this. No two-coin optimum contains a 4, so the greedy first move destroys the optimum on move one. DP survives because it never commits: it evaluates the last coin as 1, 3 and 4, and keeps the winner.
The greedy-choice property is the part you have to prove, and the standard proof is an exchange argument. Take any optimal solution. If it does not already contain your greedy choice, modify it so it does — swap your choice in, swap something else out — and show the modified solution is still valid and no worse. If that swap always works, there is always an optimum containing the greedy choice, so committing costs nothing. Peel that choice off and the same argument applies to the smaller problem left behind, which is the induction that carries you to a full proof.
State the greedy rule
One sentence, no hedging: "take the interval that finishes earliest", "take the largest coin that fits", "merge the two least-frequent symbols".
Attack it with a small input
Hunt for a counterexample before you hunt for a proof. Adversarial inputs are tiny: {1, 3, 4} and 6 is four numbers, and it is fatal.
If it survives, run the exchange
Take an optimum, swap your choice in, show it is no worse. In interval scheduling, the earliest finisher ends no later than whatever the optimum picked, so it can never collide with anything the optimum kept.
If it dies, define a state
Whatever the counterexample forced you to reconsider is your state. "Remaining amount" for coin change, "index plus remaining capacity" for knapsack. Then write the recurrence.
There is one shortcut worth knowing. Some problems are matroids — set systems where every subset of an independent set is independent, and where any smaller independent set can always absorb an element from a larger one. For a matroid with weighted elements, "sort by weight and take anything that keeps the set independent" is provably optimal, no bespoke proof required. That single theorem is why Kruskal's algorithm works: forests in a graph form a matroid, so greedily taking the cheapest edge that does not close a cycle yields a minimum spanning tree. Prim's arrives at the same optimum from a different greedy angle. If you can recognize your problem as a matroid, you are done arguing — greedy is safe.
The decision table
In practice you will not derive matroid theory at a whiteboard. You will pattern-match on the shape of the problem. These are the signals worth memorizing, each with a page on this site where the pattern is played out in full.
| Signal in the problem | Reach for | Why, and where to see it |
|---|---|---|
| Sorting on one key makes the right move obvious, and swapping it into any optimum is harmless | Greedy | Textbook exchange argument — interval scheduling sorts by earliest finish and never looks back. |
| Merging or extracting the two cheapest things is always safe | Greedy | An optimal prefix code puts the two rarest symbols deepest — Huffman coding. |
| Edge weights are non-negative and you can permanently finalize the nearest unfinished node | Greedy | No later path can undercut a settled distance — Dijkstra. Add a negative edge and this collapses. |
| The valid sets form a matroid (forests, independent sets, spanning structures) | Greedy | Greedy is optimal by theorem — Kruskal and Prim. |
| You must hit an exact total, and a big early grab can strand an awkward remainder | DP | The {1, 3, 4} counterexample above — coin change. |
| A hard capacity, and value does not line up with the resource it consumes | DP | Best value-per-weight ratio is not a safe first pick when items are indivisible — 0/1 knapsack. (Split items into fractions and greedy is optimal again.) |
| The state is a rooted subtree, and a parent's answer needs each child's answer | DP | Combine children bottom-up, one pass — tree DP. |
| The state is a subset of at most about 20 items | DP | Pack the subset into an integer and index the table with it — bitmask DP. |
| You are counting numbers in a range under a digit-wise rule | DP | Walk digit positions carrying a tight-bound flag — digit DP. |
| Subproblems do not overlap at all — each split is independent | Neither | Memoization buys nothing when nothing repeats. That is plain divide and conquer (merge sort, quicksort). |
| No optimal substructure — an optimal whole is not built from optimal parts | Neither | Longest simple path, general TSP. You are in search, branch-and-bound, or approximation territory. |
Two entries in that table deserve a second look, because they are the same problem twice. Fractional knapsack — where you may take half an item — is greedy: sort by value per unit weight, fill from the top, and the last item is cut to fit. 0/1 knapsack, where items are indivisible, is DP. Nothing changed except whether you are allowed to cut, and the entire technique flipped. Likewise, unweighted interval scheduling is greedy, but attach a value to each interval and the weighted version needs DP. When you see a problem you "know" is greedy, check whether this variant is really the variant you proved.
What each one costs
The reason to care is not elegance, it is money. A greedy algorithm is usually a sort followed by one linear pass: O(n log n) time and O(1) extra space, with a loop body of a few instructions. Dynamic programming has to materialize an answer per subproblem, so its cost is the size of the state space times the work per state. Coin change is O(n·W) time and O(W) space for n denominations and target W; 0/1 knapsack is O(n·W) for capacity W.
That W matters more than it looks. It is a value in the input, not a count of items, so a target of one billion means a billion table cells even though the input is a handful of numbers. This is what "pseudo-polynomial" means: polynomial in the numeric value, exponential in the number of bits used to write it down. A greedy solution to the same problem would not care how large the target is.
So the honest order of operations is: try greedy first. Not because it is more likely to be right, but because the payoff when it is right is enormous and the cost of checking is tiny — one adversarial example, worked by hand, in under a minute. Reach for DP when you have an actual counterexample in hand, not as a reflex. The two implementations, side by side, make the trade concrete:
Both fit on a screen. The DP is barely longer — its cost is the table, not the code. And notice what the inner loop of the DP is doing: it is trying every coin as the last coin, which is precisely the reconsideration greedy refuses to perform. That single extra loop is the difference between 3 coins and 2.
One last practical note: the two are not mutually exclusive. Many real solutions use greedy inside DP — to order states, to prune branches, to produce a bound that lets branch-and-bound discard subtrees. And a memoized recursion (top-down DP) is often the fastest way to get a correct answer you can then optimize into a bottom-up table. Correct first, cheap second.
Check yourself
1 · Greedy needs one property that DP does not. Which?
2 · Coins {1, 3, 4}, target 6. What do greedy and DP return?
3 · Fractional knapsack is greedy but 0/1 knapsack is DP. Why?
Solve it: find where greedy breaks
Greedy is faster, shorter and easier to explain — when it is correct. The only honest way to know is to attack it with a small input. Build both solvers for the same problem and let them disagree. Python or TypeScript.
Implement greedy(coins, amount) — always take the largest coin that fits — and dp(coins, amount) — the true minimum. Both return the coin count, or None/null when they cannot make the amount.
- Both solvers need optimal substructure: the best way to make 30p contains the best way to make whatever is left after the first coin. That part is shared.
- Greedy additionally assumes the greedy-choice property — that taking the locally biggest coin never rules out the global optimum. That is the assumption that fails.
- Write greedy as the obvious loop: largest coin that fits, subtract, repeat. Sort descending first so "largest that fits" is a single scan.
- Greedy can get stuck, not merely be suboptimal: with coins {3, 4} and a target of 6 it takes 4, is left with 2, and reports failure — while 3+3 exists.
- The DP state is "fewest coins to make exactly
a", for everyafrom 0 up to the target. Each answer is 1 + the best of every reachable smaller amount. - Use a sentinel for "unreachable" and only build on amounts that were actually reachable — otherwise you will happily construct answers out of impossible states.
- On {25, 10, 5, 1} greedy is provably optimal, which is exactly why testing it only on real coins would hide the bug. Change the denominations and it dies.
- The lesson is not "greedy is bad". It is that greedy needs a proof (or a surviving attack); DP only needs a correct state.
Questions
When is a greedy algorithm guaranteed to be correct?
When the problem has optimal substructure — an optimal whole is built from optimal parts, which DP needs too — and the greedy-choice property: the locally best move is contained in at least one globally optimal solution. The second one is what you have to prove, usually with an exchange argument (take an optimum, swap your choice in, show it is no worse). If the structure happens to be a matroid, the greedy-choice property comes for free by theorem.
Why does greedy fail on coins {1, 3, 4}?
Because taking the biggest coin that fits leaves the worst possible remainder. For 6, greedy takes 4 and is left with 2 — the one amount this set makes expensively, at two coins. Total: three. DP tries 1, 3 and 4 as the last coin and finds 3 + 3 for two. The coin set has optimal substructure but not the greedy-choice property, since no two-coin optimum for 6 contains a 4 at all.
Is greedy always faster than dynamic programming?
Almost always, and often by a large margin. Greedy is typically a sort plus a linear pass — O(n log n) time, O(1) space. DP stores an answer per subproblem: O(n·W) time and O(W) space for coin change or knapsack, which is pseudo-polynomial (it scales with the numeric value of the target, not with the size of the input text). That gap is exactly why it is worth spending a minute trying to break a greedy rule before you reach for a table.
How do I decide during an interview?
Say the greedy rule out loud immediately, then spend a minute attacking it with a tiny adversarial input, the way {1, 3, 4} with target 6 kills biggest-coin-first. If you cannot break it, sketch the exchange argument. If you break it, the thing you had to reconsider is your DP state — write the recurrence from there. Naming a failed greedy and the counterexample that killed it is a strong signal on its own: it shows the DP is necessary rather than reflexive.
What do greedy and DP actually have in common?
Optimal substructure. That shared requirement is why the same problem so often admits both, and why the boundary is subtle — fractional versus 0/1 knapsack, unweighted versus weighted interval scheduling. The difference is only what happens at a decision point: DP tries every option and keeps the best (paying for a table), greedy takes one without looking (paying nothing), which is sound only when that option is provably safe.
Try greedy. Then try to break it.
If a tiny adversarial input beats your rule, the thing you were forced to reconsider is your DP state. If nothing beats it, prove the exchange and enjoy the O(n log n).
Prefer a video walkthrough?
Explore the topic
See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.
More Algorithms
- Six Degrees: Union-FindDon't memorize the Disjoint Set Union — play it. Wire up nine strangers one handshake at a time and watch separate friend circles merge into one, hit a redundant connection and see it refuse to close a cycle, then click any node to run find and watch path compression flatten the tree so the next lookup is instant. Union by rank, path compression and the near-constant O(α(n)) that powers Kruskal's MST — plus full theory, a runnable challenge and a quiz.Read →
- The Typo Fixer: Edit DistanceDon't memorize edit distance — watch the grid fill in. See how many insert, delete, and replace edits turn kitten into sitting, as a dynamic-programming table computes each cell from its diagonal, up, and left neighbors, then traces the cheapest path of edits back through the grid. The Levenshtein recurrence, match-is-free, and O(m·n) — plus full theory, a runnable challenge and a quiz.Read →
- The Cheapest Grid: Kruskal's MSTDon't memorize Kruskal's algorithm — watch it wire up a network. Connect eight towns for the least total cable by always laying the cheapest edge that doesn't close a loop, with Union-Find rejecting cycles on the fly and the minimum spanning tree turning green edge by edge. Greedy choice, the cut property, and O(E log E) — plus full theory, a runnable challenge and a quiz.Read →
- The Patient Router: Bellman-FordDon't memorize Bellman-Ford — watch the distances settle. Find shortest paths from one source even with negative edge weights by relaxing every edge round after round until nothing improves, then one extra pass to catch a negative cycle. See why Dijkstra's greedy commit breaks on negatives while Bellman-Ford's patience doesn't — O(V·E), plus full theory, a runnable challenge and a quiz.Read →
- The Non-Backtracker: KMPDon't memorize KMP — watch the pattern slide. Find a pattern in a text in linear time by never re-reading a character: build the pattern's LPS failure table from its own repeats, then on a mismatch jump the pattern forward instead of restarting the text pointer. See the false start, the jump, and the match — plus the O(n+m) theory, a runnable challenge and a quiz.Read →
- The Word Tree: TrieDon't memorize the trie — watch words grow into a tree. Insert CAT, CAR, CARD, and DOG letter by letter and see shared prefixes share a path, with a flag marking where each real word ends. Then search a word and a prefix by walking down from the root. The O(length) prefix tree behind autocomplete — plus full theory, a runnable challenge and a quiz.Read →