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?
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
- Dijkstra: The Last MileDon't watch Dijkstra's algorithm — play it. Drive a courier through a living isometric city, lose the fastest route to your own instincts, then meet the Dispatcher who floods the streets to find the optimal path every time. Four acts: drive it, watch the frontier, predict the next lock, then break it with a negative cycle.Read →
- Binary Search: The VaultDon't memorize binary search — play it. Crack a vault of sorted dials, burn through guesses by instinct, then meet the Halver who throws away half the search space with every single look and finds any value in O(log n). Three acts — crack it, watch the window collapse, predict the midpoint.Read →
- Quicksort: The Pivot PitSee quicksort actually work — then drive it. Watch the pivot partition an array in place, smaller-left and larger-right; pick your own pivot and feel how its position sets the balance; then feed it an already-sorted list and watch a fixed pivot melt down to O(n²) — and fix it with one random line. Three acts — watch the pivot, pick the pivot, break the worst case.Read →
- Merge Sort: The CascadeDon't read about merge sort — play it. Watch single-element runs cascade upward into one sorted array, merge two sorted halves by hand by always taking the smaller front, then prove the payoff: O(n log n) on sorted, reversed and shuffled input alike — the guarantee quicksort can't make. Three acts — watch the cascade, merge two runs, prove the guarantee.Read →
- BFS: The FloodDon't memorize breadth-first search — play it. Find your own way through a maze, then release the flood that spreads from the start in rings and touches the exit by the shortest path every time, then watch depth-first search dive deep and miss it. Three acts — navigate it, release the flood, BFS vs DFS.Read →
- A* Search: The AscentDon't memorize A* — fly it. Orbit a real 3D mountain, climb it by instinct, then meet the Pathfinder who folds one optimistic guess into Dijkstra's flood so the whole search leans straight at the summit. Four acts in live 3D — climb it, watch the heuristic beat the blind flood, predict the lowest-f pop, then break optimality with a heuristic that lies.Read →
Explore more from Vibe Engines
Get the next one in your inbox.
New handbooks, system-design walkthroughs, and tools — straight to your inbox. No spam, unsubscribe anytime.