LeetCode #322 Medium

Coin Change

Coin Change is LeetCode 322 (Medium). You are given an array coins of distinct denominations and a target amount. You may use as many coins of each denomination as you like. Return the fewest coins whose values add up to exactly amount.

If no mix of coins reaches the amount, return -1.

Constraints
  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 2³¹ - 1
  • 0 <= amount <= 10⁴
dpbfsarray
Open on LeetCode ↗
02

Intuition

The tempting move is greedy: keep taking the largest coin that fits. It works for everyday currency but fails for arbitrary coin sets, because a big coin now can leave an awkward remainder.

What always works is to look at the last coin of an optimal answer. If it is c, the coins before it must make amount - c with as few coins as possible, otherwise a better set for that remainder would beat the optimum. So the best count for any amount is one more than the best count for some smaller amount, and trying every coin as the last one finds it. That is dynamic programming coin change: small amounts are solved first and reused.

How to spot this pattern

Minimise the number of items that sum to a target, with each item reusable any number of times. That is the unbounded knapsack shape. The state is just the amount left, and every coin is retried at every amount. It is also a shortest path: amounts are nodes, each coin is an edge, and you want the fewest edges from 0 to amount.

03

Approach

Try it first

Before reading on, check greedy on coins [1, 3, 4] and amount 6. Then decide what dp[a] should mean and which earlier entries it needs. Aim for O(amount × coins) time.

1

See why greedy is not enough

Largest-first picks 4, 1, 1 for amount 6 with coins [1, 3, 4]; the optimum is 3, 3. A choice that looks best now can leave an awkward remainder, so every option has to be compared.

2

Define the state

dp[a] = the minimum number of coins that sum to exactly a, for every a from 0 to amount. Storing every smaller amount, not just the target, is what lets each answer be built from earlier ones instead of being recomputed from scratch.

3

Set the base case and the impossible marker

  • dp[0] = 0: zero coins make zero.
  • Every other entry starts above any real answer: infinity, or amount + 1, since no answer can use more than amount coins of value 1.

That marker means "not reachable yet", and it survives every min until a real count replaces it.

4

Fill amounts from 1 upward

For each amount a from 1 upward, and each coin c <= a, set dp[a] = min(dp[a], dp[a - c] + 1). Because a - c < a, that entry is already final when it is read. Adding 1 to the unreachable marker still loses every min, so impossible amounts stay impossible.

5

Return the answer or -1

If dp[amount] is still the impossible marker, return -1; otherwise return dp[amount]. To list the coins, walk back from amount, each time subtracting a coin c with dp[a - c] = dp[a] - 1.

04

Coin Change solution in Python | C++ | Java

▶1class Solution:
▶2 def coinChange(self, coins: List[int], amount: int) -> int:
▶3 INF = amount + 1
▶4 dp = [0] + [INF] * amount
▶5 for a in range(1, amount + 1):
▶6 for c in coins:
▶7 if c <= a:
▶8 dp[a] = min(dp[a], dp[a - c] + 1)
▶9 return dp[amount] if dp[amount] <= amount else -1
001234567891011
coins[1, 2, 5]unlimited supply of each
dp[0]0zero coins make amount 0
Define the table. dp[a] = the fewest coins that add up to exactly a. Only dp[0] is known: zero coins make zero. Every other amount is filled from smaller amounts, because the last coin you add is some c, and the rest must make a - c as cheaply as possible.
amount1coin 1 → 10011234567891011+1
amount1coins that fit: 1
dp[1]1best of the tries
Try each coin as the last one: dp[0]+1 = 1. The minimum is 1. Every smaller amount is already final, so this value is final too.
amount2coin 1 → 2coin 2 → 100111234567891011+1+2
amount2coins that fit: 1, 2
dp[2]1best of the tries
Try each coin as the last one: dp[1]+1 = 2, dp[0]+1 = 1. The minimum is 1. Every smaller amount is already final, so this value is final too.
amount3coin 1 → 2coin 2 → 2001112234567891011+1+2
amount3coins that fit: 1, 2
dp[3]2best of the tries
Try each coin as the last one: dp[2]+1 = 2, dp[1]+1 = 2. The minimum is 2. Every smaller amount is already final, so this value is final too.
amount4coin 1 → 3coin 2 → 20011122324567891011+1+2
amount4coins that fit: 1, 2
dp[4]2best of the tries
Try each coin as the last one: dp[3]+1 = 3, dp[2]+1 = 2. The minimum is 2. Every smaller amount is already final, so this value is final too.
amount5coin 1 → 3coin 2 → 3coin 5 → 100111223241567891011+1+2+5
amount5coins that fit: 1, 2, 5
dp[5]1best of the tries
Try each coin as the last one: dp[4]+1 = 3, dp[3]+1 = 3, dp[0]+1 = 1. The minimum is 1. Every smaller amount is already final, so this value is final too.
amount6coin 1 → 2coin 2 → 3coin 5 → 2001112232415267891011+1+2+5
amount6coins that fit: 1, 2, 5
dp[6]2best of the tries
Try each coin as the last one: dp[5]+1 = 2, dp[4]+1 = 3, dp[1]+1 = 2. The minimum is 2. Every smaller amount is already final, so this value is final too.
amount7coin 1 → 3coin 2 → 2coin 5 → 20011122324152627891011+1+2+5
amount7coins that fit: 1, 2, 5
dp[7]2best of the tries
Try each coin as the last one: dp[6]+1 = 3, dp[5]+1 = 2, dp[2]+1 = 2. The minimum is 2. Every smaller amount is already final, so this value is final too.
amount8coin 1 → 3coin 2 → 3coin 5 → 300111223241526273891011+1+2+5
amount8coins that fit: 1, 2, 5
dp[8]3best of the tries
Try each coin as the last one: dp[7]+1 = 3, dp[6]+1 = 3, dp[3]+1 = 3. The minimum is 3. Every smaller amount is already final, so this value is final too.
amount9coin 1 → 4coin 2 → 3coin 5 → 3001112232415262738391011+1+2+5
amount9coins that fit: 1, 2, 5
dp[9]3best of the tries
Try each coin as the last one: dp[8]+1 = 4, dp[7]+1 = 3, dp[4]+1 = 3. The minimum is 3. Every smaller amount is already final, so this value is final too.
amount10coin 1 → 4coin 2 → 4coin 5 → 20011122324152627383921011+1+2+5
amount10coins that fit: 1, 2, 5
dp[10]2best of the tries
Try each coin as the last one: dp[9]+1 = 4, dp[8]+1 = 4, dp[5]+1 = 2. The minimum is 2. Every smaller amount is already final, so this value is final too.
amount11coin 1 → 3coin 2 → 4coin 5 → 300111223241526273839210311+1+2+5
amount11coins that fit: 1, 2, 5
dp[11]3best of the tries
Try each coin as the last one: dp[10]+1 = 3, dp[9]+1 = 4, dp[6]+1 = 3. The minimum is 3. Every smaller amount is already final, so this value is final too.
dp551001112232415262738392103113 coins → return 3
answer3fewest coins
greedy3largest coin first
Answer 3. Walking back from 11 through amounts whose value is one less recovers the coins: 5 + 5 + 1.
05

Common pitfalls

Using greedy (largest coin first)

✗ Wrong
for c in sorted(coins, reverse=True):
    count += amount // c
    amount %= c
✓ Right
for a in range(1, amount + 1):
    for c in coins:
        if c <= a:
            dp[a] = min(dp[a], dp[a - c] + 1)

Greedy is only correct for special coin systems. With [1, 3, 4] and 6 it returns 3 instead of 2, and with [2, 5] and 6 it gets stuck at 1 left over although 2 + 2 + 2 works.

Initialising the table with 0

✗ Wrong
dp = [0] * (amount + 1)
✓ Right
dp = [0] + [amount + 1] * amount

With zeros, min(dp[a], ...) keeps 0 and every amount looks free. Unknown amounts need a value larger than any real answer.

Adding 1 to infinity in a fixed-size integer

✗ Wrong
vector<int> dp(amount + 1, INT_MAX);
// dp[a - c] + 1 overflows
✓ Right
vector<int> dp(amount + 1, amount + 1);

INT_MAX + 1 wraps to a negative number, which then wins every min. amount + 1 is safely larger than any answer and cannot overflow.

06

Edge cases

amount = 0

Return 0: no coins are needed. The loop does not run and dp[0] = 0 is returned.

Amount cannot be made

For example coins [2], amount 3. dp[3] never improves from the marker, so the answer is -1.

Large coin values

Coins bigger than the current amount are skipped by the c <= a check, so values up to 2^31 - 1 are safe.

07

Complexity

Time
O(amount × n)
Space
O(amount)
n is the number of coin types. The time is pseudo-polynomial: it grows with the value of amount, not the size of the input.
08

Coin change vs related problems

Same coins, different question. The question decides both the recurrence and the loop order.

ProblemQuestionRecurrenceLoop order
Coin Change (322)fewest coinsdp[a] = min(dp[a - c] + 1)either order works
Coin Change II (518)number of combinationsdp[a] += dp[a - c]coins outer, amounts inner
Combination Sum IV (377)number of ordered sequencesdp[a] += dp[a - c]amounts outer, coins inner
Greedy changefewest coins, canonical systems onlytake the largest coin that fitsno table; wrong for [1, 3, 4]
09

Coin Change FAQ

What is the coin change problem?

Given coin denominations with unlimited supply and a target amount, find the minimum number of coins that add up exactly to the amount, or report that it is impossible. For coins [1, 2, 5] and amount 11 the answer is 3 (5 + 5 + 1).

What is the dynamic programming coin change solution?
  • State: dp[a] = minimum coins to make amount a.
  • Base case: dp[0] = 0; all other entries start at infinity.
  • Recurrence: dp[a] = min over coins c <= a of dp[a - c] + 1.
  • Order: fill a = 1, 2, ..., amount.
  • Answer: dp[amount], or -1 if it is still infinity.
  • Complexity: O(amount × n) time, O(amount) space.
  • Example: coins [1, 2, 5], amount 11 gives 3.
Why does greedy fail for coin change?

Taking the largest coin first can leave a remainder that needs many small coins. With [1, 3, 4] and 6, greedy gives 4 + 1 + 1 = 3 coins, but 3 + 3 = 2. Greedy is only guaranteed for canonical systems such as [1, 5, 10, 25].

What is the coin change recursive solution?

f(a) = 0 if a == 0, infinity if a < 0, otherwise 1 + min(f(a - c)) over all coins. Without memoisation it is exponential; caching f(a) gives the same O(amount × n) as the table.

What is the difference between Coin Change and Coin Change II?

Coin Change asks for the fewest coins (a min recurrence). Coin Change II asks for the number of combinations that make the amount (a += recurrence), and there the loop order matters: coins in the outer loop counts each combination once.