Coin Change II

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

Problem

You have an unlimited supply of each coin denomination in coins. Count how many different combinations of coins add up exactly to amount. Order does not matter: 1+2 and 2+1 are the same combination. If no combination works, return 0.
Inputamount=5, coins=[1,2,5]Output4
Inputamount=3, coins=[2]Output0
Inputamount=10, coins=[10]Output1

1 ≤ coins.length ≤ 300  ·  1 ≤ coins[i] ≤ 5000 (distinct)  ·  0 ≤ amount ≤ 5000  ·  answer fits in a 32-bit int

Try it yourself

Java
class Solution {
    public int change(int amount, int[] coins) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Avoid counting permutations by deciding coins one type at a time: put the coin loop outside the amount loop. Then every combination is built in a fixed coin order, so dp[j] += dp[j - coin] counts each one exactly once.
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 ForceRecursion (Take / Skip Coin)

Time O(2n + A)Space O(n + A)

At coin index either skip it for good (move to index+1) or take one more of it (stay on index, reduce the amount). Fixing the coin order this way never produces the same combination twice. Each call moves the index or lowers the amount, so depth ≤ n + A (A = amount).

Java
public int change(int amount, int[] coins) {
    return countWays(coins, amount, 0);
}
private int countWays(int[] coins, int remaining, int index) {
    if (remaining == 0) return 1;                       // valid combination
    if (remaining < 0 || index == coins.length) return 0;
    int skip = countWays(coins, remaining, index + 1);
    int take = countWays(coins, remaining - coins[index], index);
    return skip + take;
}

Correct and order-safe, but the same (index, remaining) states are recomputed exponentially.

At a glance

LevelApproachTimeSpace
Brute ForceRecursion (Take / Skip Coin)O(2n + A)O(n + A)
Better2D DP TableO(n · A)O(n · A)
Optimal1D DP (Coins Outer Loop)O(n · A)O(A)
Common pitfalls
  • Amount loop outside the coin loop — counts 1+2 and 2+1 separately.
  • Iterating j downward in 1D — turns it into 0/1 knapsack (each coin once).
  • Forgetting dp[0] = 1 — every answer becomes 0 (and amount=0 must return 1).
Interview tips
  • Contrast with Coin Change (322): min coins uses min, this one uses +.
  • Explain why the loop order matters before writing code.
  • Derive 1D from 2D by noting which cells each row reads.
Pattern
Unbounded knapsack (counting) → items reusable, count combinations reaching an exact target.
My notes saved in this browser