Dynamic programming · Unbounded knapsack
coins with the coin values, and an integer amount. You have an unlimited supply of each coin. Return the fewest coins that add up to exactly amount. If no combination works, return -1.
Picture a shop till. You have an endless supply of coins of a few values, and you must pay exactly a given amount using as few coins as you can. If the amount cannot be paid exactly, the answer is -1.
The rules, in three steps:
-1 if it is impossible.A natural first idea is greedy: always take the biggest coin that still fits. It feels right, so test it. Take coins [1, 3, 4] and the amount 6.
Greedy takes the 4 first, then is stuck with 2 left, so it pays 1 + 1. That is three coins in total. But 3 + 3 does it with two coins, and never uses the biggest coin. So greedy is not safe, and we need something that really checks the choices.
The honest way to check every choice: try each coin as the last coin you hand over, and solve what is left by the same method.
Every call splits into one call per coin, and the calls go as deep as the amount. That is a huge number of calls, far too many for an amount like 10,000.
It also repeats itself. Paying 3 then 1 and paying 1 then 3 both leave the same remainder of 2, and the code solves "what is the best way to pay 2?" again each time.
Look at the part we keep repeating: "the fewest coins to pay this smaller amount". If we worked that out once for every amount from 0 up to the target and wrote the answers in a table, we could simply look them up. So what is the best answer for each smaller amount, and how does each one use the ones before it?
Take any way of paying amount a. It ends with some last coin c. Take that coin away, and the rest pays a − c. That rest must itself be a best way to pay a − c. If it were not, we could swap in a better one and beat our own answer. So the best way to pay a is always one coin on top of the best way to pay a smaller amount.
We do not know which coin is last, so we try every coin c that is not bigger than a, and keep the smallest result. Write dp[a] for the fewest coins that pay a:
dp[a] = 1 + min(dp[a − c]) over every coin c ≤ a.dp[0] = 0, because paying nothing takes no coins.amount + 1, more than any real answer). Adding 1 to it never wins a min, so an impossible amount stays impossible.dp[amount], or -1 if it is still the huge value.dp[0] = 0 and every other cell to the huge value.a from 1 up to the target, try every coin c.dp[amount], or -1 if it is still the huge value.By hand, with coins [1, 3, 4] and amount 6:
Press Play, or use the arrow keys, to watch the table fill. The first input is this same example. Watch dp[3] and dp[6]. The order of the two loops does not matter here, because each cell reads only smaller amounts that are already final.
These are the questions that usually come next. Open one to see an answer you could say out loud.
What they are checkingWhether you understand why the order of the two loops matters. Counting is a close cousin of this problem, and the loop order is where most people slip.
A strong answerI swap min for adding, and set dp[0] = 1, since there is one way to pay nothing. The important change: the coin loop goes on the outside and the amount loop on the inside. Then each coin is "used up" in turn, so 1 + 2 and 2 + 1 count as one way. With the amount loop on the outside, they would be counted as two different orders.
What they are checkingWhether you can carry an answer along with a number. This is a very common follow-up for table-filling problems.
A strong answerNext to dp I keep a second list, pick[a], holding the last coin that gave the best value for amount a. At the end I start at the target, write down pick[amount], subtract it, and repeat until I reach 0. That costs only as many steps as there are coins in the answer.
What they are checkingWhether you notice that the table size depends on the amount, and whether you can say what changes when the limits change.
A strong answerNo. A table with a billion cells is too big for memory and too slow to fill. With only a few coin values, the question becomes a number puzzle instead: I would look at the structure of the coin values, for example how large coins can be repeated and then fixed up with the small ones. I would not guess a formula in the interview. I would say the table does not scale and ask for the exact limits first.