Run it
The case that separates greedy from optimal.
And the currency where greedy is safe, checked across a hundred amounts.
The edges, and the word that matters about the complexity.
The counterexample is the answer
Almost everyone offers greedy first, because it is what people do with actual money and it works. The single most useful thing to have ready is the smallest case where it fails: coins 1, 3, 4 and amount 6. Greedy takes 4, then can only use 1s, and ends at three coins. The optimum is 3 + 3.
Offering that unprompted is worth more than the DP itself, because it shows you tested the obvious approach instead of assuming it. And it explains why the intuition is so strong: greedy is optimal for canonical coin systems, which is what every real currency is designed to be. The editor checks [1, 5, 10, 25] and greedy matches the DP on every amount.
The recurrence, and the direction to fill it
Partition by the last coin. If the final coin used is c, the rest of the amount cost best[a - c] — so best[a] = 1 + min(best[a - c]) over every coin that fits.
Fill upwards from best[0] = 0, because every entry depends only on smaller ones. That ordering is the whole of bottom-up DP and it is why no recursion is needed: by the time you reach a, everything it depends on is already final.
Use float("inf") for unreachable amounts rather than a sentinel like −1. Infinity survives the min and the + 1 correctly; −1 does not, and mixing the sentinel into the arithmetic is the standard bug in this problem.
Fewest coins versus how many ways
Two different questions share this setup and the difference is a loop order, which is worth knowing because interviewers switch between them.
Fewest coins — this page. The loops can go in either order, because min does not care.
How many ways to make the amount — ways[0] = 1 and ways[a] += ways[a - c]. Here the order decides what you are counting: coins on the outside and amounts inside counts combinations (order does not matter); amounts outside and coins inside counts permutations. It is the same two loops swapped, and the results differ — a genuinely nasty detail and a favourite follow-up.
What it costs, and when it is too much
O(amount × coins) time and O(amount) space. Note that amount is a value, not an input length, so the table is exponential in the number of bits of the input — this is a pseudo-polynomial algorithm, and coin change with unbounded denominations is NP-hard in general.
In practice that means it is fine for amounts in the thousands and wrong for amounts in the billions. Saying "O(amount times coins), and note amount is the value so this is pseudo-polynomial" is a strong close, because it shows you know the difference between the size of an input and the magnitude of it.
What to say out loud
Greedy does not work in general - with coins 1, 3, 4 and amount 6 it gives three coins where two is optimal. So a table: best[a] is one plus the minimum over best[a - c] for each coin c that fits, filled upwards from 0. O(amount times number of coins) time, O(amount) space. Greedy happens to be optimal for canonical systems like real currency, which is why the intuition is so strong.
Edge cases to raise
Volunteering these is most of what separates a correct answer from a good one.
Amount 0. Zero coins, not one and not an error. best[0] = 0 is the base case that makes the rest work.
Unreachable amounts. Coins of 5 and 10 cannot make 3. The answer is -1, and it has to survive the reconstruction step too.
A coin larger than the amount. Skipped by the c <= a guard. Without it the index goes negative and Python silently wraps to the end of the list - a wrong answer rather than a crash.
The follow-ups interviewers ask
"Count the number of ways instead of the fewest coins." ways[0] = 1 and ways[a] += ways[a - c] - and now the loop order decides what you count. Coins outside counts combinations; amounts outside counts permutations. Same two loops, different answers.
"Return the actual coins, not just the count." Record which coin won at each amount and walk the chain back from the target. The editor does it, and it is asked for more often than not.
"What if each coin can be used only once?" That is 0/1 knapsack, and the fix is the loop direction: iterate the amount downwards so each coin is considered once per amount. Getting that direction wrong is the classic knapsack bug.
Common wrong answers
"Greedy: take the largest coin that fits." Wrong in general, and the counterexample to have ready is coins 1, 3, 4 for amount 6 - greedy gives three coins, optimal is two.
"Use -1 for unreachable amounts." It corrupts the arithmetic: min picks the sentinel and +1 turns it into 0. Infinity behaves correctly.
"It is polynomial, so it scales." It is pseudo-polynomial - the table is sized by the amount's value, so adding a digit multiplies the work by ten. Fine for thousands, hopeless for billions.
Recap in one screen
- Every entry is built from a smaller amount, which is why filling upwards works.
- Greedy commits to the 4 and cannot undo it.
- Unreachable amounts stay at infinity and become -1.
- Worth trying: Find another coin set where greedy fails. [1, 5, 12] for 15 is one - greedy gives 12+1+1+1, four coins, against 5+5+5.
- Worth trying: Change it to count the number of ways instead, then swap the two loops. The answers differ, and working out which one counts combinations is the standard follow-up.