Coin Change

MediumDynamic Programming · Unbounded Knapsack🧩 Knapsack DPLeetCode #322 ↗Updated

Problem

You have coin denominations coins, each available in unlimited quantity. Return the fewest coins whose values add up exactly to amount, or -1 if that amount cannot be formed.
Inputcoins=[1,2,5], amount=11Output3 (5+5+1)
Inputcoins=[2], amount=3Output-1
Inputcoins=[1], amount=0Output0

1 ≤ coins.length ≤ 12  ·  1 ≤ coins[i] ≤ 231-1  ·  0 ≤ amount ≤ 104

Try it yourself

Java
class Solution {
    public int coinChange(int[] coins, int amount) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Greedy (largest coin first) fails, e.g. [1,3,4], 6. Instead, the last coin used is some c, so best(a) = 1 + minc best(a - c) — and there are only amount+1 distinct sub-amounts.
PatternKnapsack DP

Instinct Choose items to hit or stay under a target sum (can it be done, best value, fewest items, how many ways) - index a DP array by the remaining capacity.

Memory hack Once? Walk back. Many? Walk forward. Each item used at most once loops capacity downward; reusable items loop it upward.

Open the Knapsack DP guide →

Approaches

Brute ForcePlain Recursion over Last Coin

Time O(kA)Space O(A)

For remaining amount rem, try every coin as the next one and recurse on rem - c; keep the minimum. With k coins and depth up to A = amount (coin 1), the tree has up to kA nodes because sub-amounts repeat endlessly.

Java
public int coinChange(int[] coins, int amount) {
    if (amount == 0) return 0;
    if (amount < 0) return -1;
    int best = Integer.MAX_VALUE;
    for (int c : coins) {
        int sub = coinChange(coins, amount - c);
        if (sub >= 0) best = Math.min(best, sub + 1);
    }
    return best == Integer.MAX_VALUE ? -1 : best;
}

Correct, but hopeless for amounts beyond ~30.

At a glance

LevelApproachTimeSpace
Brute ForcePlain Recursion over Last CoinO(kA)O(A)
BetterTop-Down MemoizationO(k·A)O(A)
OptimalBottom-Up 1-D TabulationO(k·A)O(A)
Common pitfalls
  • Greedy by largest coin — not optimal for arbitrary denominations.
  • Using Integer.MAX_VALUE as infinity, then +1 → overflow.
  • Memo sentinel clash: don't use -1 for both “unknown” and “impossible”.
Interview tips
  • Show a greedy counter-example first to justify DP.
  • State the recurrence via the last coin; note it's unbounded knapsack (forward loop).
  • Contrast with Coin Change II (count ways): swap the loop order to avoid counting permutations.
Pattern
Unbounded knapsack (min items) → reach an exact total with reusable items using the fewest picks.
My notes saved in this browser