Algorithm

Coin Change II (Ways)

Dynamic Programming Pattern

Coin Change II

You are given an integer amount and an array coins of distinct positive denominations, each available in unlimited supply. Return the number of distinct combinations of coins that add up to exactly amount. Two selections count as the same combination if they use the same number of each denomination — order does not matter, so 1+2 and 2+1 are one combination, not two. If the amount cannot be made, return 0. If the amount is 0, return 1, counting the empty selection.

CONSTRAINTS
  • 1 ≤ coins.length ≤ 300
  • 1 ≤ coins[i] ≤ 5000
  • 0 ≤ amount ≤ 5000
  • All denominations are distinct and available in unlimited quantity
  • The answer fits in a signed 32-bit integer
EXAMPLE 1
Input: amount = 5, coins = [1, 2, 5]
Output: 4
The combinations are 1+1+1+1+1, 1+1+1+2, 1+2+2, and 5. Rearrangements such as 2+1+2 are not counted separately because they use the same coins.
EXAMPLE 2
Input: amount = 3, coins = [2]
Output: 0
Every total built from 2s is even, so 3 is unreachable and there are no combinations at all.
EXAMPLE 3
Input: amount = 0, coins = [1, 2]
Output: 1
Taking no coins is one valid way to make zero. Returning 0 here would be wrong, and it is also the base value the whole table is built on.
EXAMPLE 4
Input: amount = 3, coins = [1, 2]
Output: 2
Only 1+1+1 and 1+2 exist. Writing the second as 2+1 does not add a third answer, since a combination is defined by how many of each coin it uses.
EXAMPLE 5
Input: amount = 10, coins = [10]
Output: 1
A single coin matching the amount exactly is one combination, and no other selection of 10s can total 10.
Does the order of coins within a selection matter?
No — this is the decisive clarification. 1+2 and 2+1 are the same combination and are counted once. If the interviewer says order does matter, the problem becomes a different one, solved with the amount loop on the outside.
What should I return when the amount is 0?
1, for the empty selection. This is not a special case bolted on afterwards; it is the base value that makes every other count come out right.
Are the coin denominations distinct?
Yes. Duplicates in the input would cause the same combination to be counted more than once, so if duplicates were possible you would need to remove them before starting.
Could the count overflow?
The statement guarantees the answer fits in a signed 32-bit integer, but the count grows very fast in general, so it is worth confirming. If the guarantee were removed you would ask whether the answer should be taken modulo some number.
The word that changes everything

You have coins of a few fixed values, each in unlimited supply. Count how many different combinations add up to an exact amount. The catch is one word: order does not matter.

For amount = 3 with coins = [1, 2], the answer is 2, not 3. The combinations are {1,1,1} and {1,2}. Writing the second as 2+1 is the same handful of coins, not a new answer.

This is the whole difficulty. If we are not careful, we count 2+1 and 1+2 as two and get the wrong number. Let us see exactly where that slip happens.

The wrong recurrence, and how to spot it

A first attempt: let g(a) be the number of ways to make a, and say the last coin was some c:

g(a) = sum over every coin c <= a of g(a - c)

Run it on coins = [1, 2], amount = 3: g(0)=1, g(1)=1, g(2)= g(1)+g(0) = 2, g(3) = g(2)+g(1) = 3. It says 3, but the true answer is 2.

Why? Because "the last coin was 1" and "the last coin was 2" describe orders. The combination {1,2} gets counted once as (a 1, then a 2) and again as (a 2, then a 1). The formula is not broken — it correctly counts ordered sequences. It just answers a different question than the one asked.

Key Insightto count unordered combinations, you must stop the same combination from being built in more than one order. The fix is to fix an order and only ever build combinations that way — decide about coin 0 fully, then coin 1, then coin 2, and never go back. Then each combination is built exactly one way, so nothing is counted twice.
The state, as a story

I go through the coins one at a time, from the last toward the first — a fixed order, so I never count the same combination twice. Standing at coin i with amount a still to make, I have two moves:

- Use one coin i (only if coins[i] <= a) — I pay it down but stay on coin i, because I may want more copies of it: f(i, a - coins[i]).
- Skip coin i for good — move on to the coins before it: f(i-1, a).

I am counting ways, so I add the two. The two things I track are which coin I'm on (i) and how much is left to make (a).

State Definitionf(i, a) is the number of combinations that make exactly a using coins from index 0 through i.

f(i, a) = f(i-1, a) + ( f(i, a - coins[i]) if coins[i] <= a )

The staying-on-i move is how "unlimited copies" is expressed while keeping the coins in a fixed order.

Base cases:

- f(i, 0) = 1 — the amount is fully paid. Taking nothing more is one complete combination, so this counts as 1, not 0. (Set it to 0 and the whole table becomes zeros.)
- f(0, a) — only the first coin is left. You can make a only if a is an exact multiple of coins[0], by using that many copies. So it is 1 if a % coins[0] == 0, else 0.

We start by asking about the last coin with the full amount: f(n-1, amount).

Memoisation, then the table
python
def change(amount, coins):
    n = len(coins)
    memo = {}

    def f(i, a):
        if a == 0: return 1
        if i == 0: return 1 if a % coins[0] == 0 else 0
        if (i, a) in memo: return memo[(i, a)]
        ways = f(i - 1, a)                       # stop using coin i
        if coins[i] <= a:
            ways += f(i, a - coins[i])           # use another coin i; stay put
        memo[(i, a)] = ways
        return ways

    return f(n - 1, amount)

States are pairs (i, a), giving n × (amount+1) of them and O(N × amount) time.

Now the table, straight from the recurrence. Let dp[i][a] hold what f(i, a) returned, and swap each call for an array read:

dp[i][a] = dp[i-1][a] + ( dp[i][a - coins[i]] if coins[i] <= a )

The skip branch reads row i-1, so fill the rows upward. The take branch reads the same row at a smaller amount, so within a row sweep the amount upward too. The base cases copy straight over: row 0 is dp[0][a] = 1 if a % coins[0] == 0 else 0, and column a = 0 is 1 in every row:

python
def change(amount, coins):
    n = len(coins)
    dp = [[0] * (amount + 1) for _ in range(n)]

    for a in range(amount + 1):                  # row 0: only the first coin
        dp[0][a] = 1 if a % coins[0] == 0 else 0
    for i in range(n):
        dp[i][0] = 1                             # amount 0: the empty combination

    for i in range(1, n):
        for a in range(1, amount + 1):
            dp[i][a] = dp[i - 1][a]
            if coins[i] <= a:
                dp[i][a] += dp[i][a - coins[i]]

    return dp[n - 1][amount]
The one-row version, and the loop order that carries the whole idea

Row i reads row i-1 and itself, so a single array works — and the result is the version most people memorise without understanding:

python
def change(amount, coins):
    dp = [0] * (amount + 1)
    dp[0] = 1
    for c in coins:                     # coin loop OUTSIDE
        for a in range(c, amount + 1):  # amount loop inside, ascending
            dp[a] += dp[a - c]
    return dp[amount]
Crucial Notethe coin loop must be the outer loop. Swap the two — amount outside, coins inside — and you are back to counting orders, and the answer becomes 3 instead of 2 on our small example. With coins outside, every combination is built in one fixed order (all the 1s decided, then all the 2s), so it is counted once. With coins inside, you reconsider every coin at every amount, which lets the same combination be built along several routes.

Keep the pair together:

- Coins outside, amounts inside counts combinations — order ignored. (This problem.)
- Amounts outside, coins inside counts permutations — order matters. (A different question: "how many ordered sequences add up to the amount".)

The ascending amount sweep means a coin may be reused, which is exactly what unlimited supply needs.

Worked Example:amount = 5, coins = [1, 2, 5]
Start: dp = [1, 0, 0, 0, 0, 0] — one way to make 0.

Coin 1, sweeping a from 1 to 5, each dp[a] += dp[a-1]:
dp = [1, 1, 1, 1, 1, 1]. One way to make each amount, using only 1s. Correct.

Coin 2, sweeping a from 2 to 5:
- a=2: dp[2] += dp[0] → 1 + 1 = 2 ({1,1} and {2})
- a=3: dp[3] += dp[1] → 1 + 1 = 2 ({1,1,1} and {1,2})
- a=4: dp[4] += dp[2] → 1 + 2 = 3 ({1,1,1,1}, {1,1,2}, {2,2})
- a=5: dp[5] += dp[3] → 1 + 2 = 3 ({1×5}, {1,1,1,2}, {1,2,2})

Coin 5, sweeping a from 5 to 5:
- a=5: dp[5] += dp[0] → 3 + 1 = 4 (the new one is {5})

Answer 4: {1,1,1,1,1}, {1,1,1,2}, {1,2,2}, {5}. Listing them by hand confirms it.

The reflex

Whenever a problem asks "how many ways", settle one thing first, before writing anything: do two selections that differ only in order count once or twice? The answer decides your state and your loop order. Get it wrong and you produce a plausible-looking number that is not the answer to the question asked.

The cure for unordered counting is always the same: pick one fixed order to build things in, and never go back to an earlier item. Here it showed up as the coin loop on the outside; elsewhere it shows up as a start index handed down a recursion. Same idea, different costume.

Interactive Strategy Visualization

Coin Change II (Ways)

Unbounded Knapsack Variation
Coin ↓ / Amt →012345
Base
-
-
-
-
-
-
coin=1
-
-
-
-
-
-
coin=2
-
-
-
-
-
-
coin=5
-
-
-
-
-
-
Ways to Make Change: coins=[1,2,5], amount=5. Goal: Find total unique combinations.
Exponential Try Every Combination
O(Amount × Coins) Time · O(Amount × Coins) Space Memoisation
O(Amount × Coins) Time · O(Amount) Space One Row