Algorithm · Decision Framework

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.

greedy = commit · dp = remember · the difference is a proof, not a preference
GREEDY

Take the best-looking move right now, never backtrack. One cheap decision per step.

DYNAMIC PROGRAMMING

Solve every subproblem once, store the answer, build the optimum out of stored answers.

OPTIMAL SUBSTRUCTURE

The thing both need: an optimal whole is built from optimal parts.

GREEDY-CHOICE PROPERTY

The extra thing only greedy needs: the local best is inside some global optimum.

greedy-vs-dp.sim — coin change, 3 acts
Act 1 — your turn

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.

greedy rule: take the largest coin ≤ remaining, repeat
0
Coins used
6
Remaining
target = 6

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:

remaining = 6 → biggest coin that fits is 4 → take it, remaining = 2 remaining = 2 → biggest coin that fits is 1 → take it, remaining = 1 remaining = 1 → take the last 1 → remaining = 0 result: 4 + 1 + 1 = 6, three coins

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:

dp[0] = 0 dp[1] = 1 (1) dp[2] = 2 (1 + 1) dp[3] = 1 (3) dp[4] = 1 (4) dp[5] = 2 (4 + 1) dp[6] = min( dp[5] + 1, dp[3] + 1, dp[2] + 1 ) = min( 3, 2, 3 ) = 2 (3 + 3)

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.

01

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".

02

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.

03

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.

04

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 problemReach forWhy, 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.

Greedy time
O(n log n)
Greedy space
O(1)
DP time
O(n·W)
DP space
O(W)

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:

// greedy — O(n log n), no table, sometimes wrong sort(coins, descending) count = 0 for c in coins: while amount >= c: amount -= c count += 1 return amount == 0 ? count : IMPOSSIBLE // dp — O(n·W), one table, never wrong dp = [INF] * (W + 1) dp[0] = 0 for a in 1..W: for c in coins: if c <= a and dp[a - c] + 1 < dp[a]: dp[a] = dp[a - c] + 1 return dp[W] < INF ? dp[W] : IMPOSSIBLE

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?

Both need optimal substructure. Only greedy needs the greedy-choice property — the guarantee that the locally best move is contained in at least one global optimum, so committing to it rules nothing out.

2 · Coins {1, 3, 4}, target 6. What do greedy and DP return?

Greedy grabs the 4, stranding a remainder of 2 that costs two more coins. DP checks every coin as the last coin and finds 3 + 3, two coins total.

3 · Fractional knapsack is greedy but 0/1 knapsack is DP. Why?

With fractions you can always fill the remaining capacity with the best remaining ratio, so the greedy choice is provably safe. Indivisible items can leave capacity that only a worse-ratio item fits, so the top-ratio pick is no longer safe and you need the table.

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).

▶  Watch it explained

Prefer a video walkthrough?

Finished this one? 0 / 99 Algorithms done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More Algorithms