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