Start learning free

LESSON 449 OF 536 · DYNAMIC PROGRAMMING

Coin Change II in Java: Counting Combinations with 1D DP

Practice O(n · amount) Read the full explanation

You are given an integer amount, a total sum of money, and an array coins of different coin values.

Return the number of combinations of coins that add up to exactly amount. If no combination of the coins adds up to amount, return 0.

You may use each coin value as many times as you want. A combination counts how many coins of each value it uses, not their order: 2 + 2 + 1 and 1 + 2 + 2 are the same combination.

 

Example 1:

Input: amount = 5, coins = [1, 2, 5]
Output: 4
Explanation: There are four combinations:
5 = 5
5 = 2 + 2 + 1
5 = 2 + 1 + 1 + 1
5 = 1 + 1 + 1 + 1 + 1

Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: Coins of 2 only make even sums, so 3 can't be made.

Example 3:

Input: amount = 4, coins = [1, 2, 3]
Output: 4
Explanation: 4 = 1 + 1 + 1 + 1 = 2 + 1 + 1 = 2 + 2 = 3 + 1. Counting different orders as different, such as 3 + 1 and 1 + 3, would give 7 instead.

 

Constraints:

  • 1 <= coins.length <= 300
  • 1 <= coins[i] <= 5000
  • All the values in coins are different.
  • 0 <= amount <= 5000 (when amount is 0, the answer is 1: the combination of no coins at all)
  • The answer fits in a 32-bit signed integer, and so does the number of combinations for every smaller amount.

Note:

Your algorithm should run in O(n · amount) time and O(amount) space, where n is the number of coins.



Listing every combination is far too slow: a few dozen coins can make more than a billion combinations of 5000. Count them instead: let ways[a] be the number of combinations that make the sum a, for every a from 0 to amount. Start with ways[0] = 1, the combination of no coins, and every other entry at 0. The answer is ways[amount].

Add the coins one at a time, in an outer loop. For each coin, go over the sums a from coin up to amount and add ways[a - coin] to ways[a]: every combination for a - coin, plus one more of this coin, is a combination for a. Going up lets ways[a - coin] already contain this coin, so it can be used many times.
Why the coins must be the outer loop: when a coin is added, the counts so far only use the coins before it. So every combination is built in one fixed order, first all its coins of the first value, then all of the second, and so on, and it is counted exactly once: 1 + 3 is counted, 3 + 1 never is. With the sums outside and the coins inside, any coin could be the last one added to any sum, so 1 + 3 and 3 + 1 would both be counted: that counts orders, not combinations.

Each of the n coins goes over at most amount + 1 sums, with a constant amount of work for each: O(n · amount) time. ways holds amount + 1 counts: O(amount) space.
STEP 1 OF 6
    Ctrl + Enter to check

    JAVA · DYNAMIC PROGRAMMING

    Coin Change Ii in Java, explained

    Problem overview

    The method change(int amount, int[] coins) receives a target sum and an array of different coin values. It returns how many combinations of coins add up to exactly amount, or 0 if none do. Each coin value can be used any number of times. A combination counts how many coins of each value it uses, not their order, so 2 + 2 + 1 and 1 + 2 + 2 are the same combination.

    With amount = 5 and coins = [1, 2, 5] the answer is 4: 5, 2 + 2 + 1, 2 + 1 + 1 + 1 and 1 + 1 + 1 + 1 + 1. With amount = 3 and coins = [2] the answer is 0, because a 2-coin only makes even sums. With amount = 0 the answer is 1: the combination of no coins at all.

    Approach: count sums, one coin at a time

    Listing every combination is hopeless for large inputs: a few dozen coins can make over a billion combinations of 5000. Counting is enough, and the counts for small sums can be reused for big ones.

    Let ways[a] be the number of combinations that make the sum a, for every a from 0 to amount. Start with ways[0] = 1 (no coins) and every other entry at 0. The answer is ways[amount].

    Now add the coins one at a time. When a coin is added, every combination for the sum a - coin, plus one more of that coin, is a combination for a. So ways[a] += ways[a - coin].

    The order of the two loops decides what is counted. With the coins in the outer loop, each combination is built in one fixed order: all its coins of the first value, then all of the second, and so on. It is therefore counted exactly once, and 1 + 3 is counted but 3 + 1 never is. If the sums were outside and the coins inside, any coin could be the last one added to any sum, and 1 + 3 and 3 + 1 would both be counted. That counts orders, not combinations: for amount = 4 and coins = [1, 2, 3] the combinations number 4, and counting orders gives 7.

    The inner loop goes upward from coin. Going up means ways[a - coin] may already include the current coin, which is what lets one coin value be used again and again.

    The table after each coin

    For amount = 5 and coins = [1, 2, 5], the array ways (indexes 0 to 5) looks like this after each coin:

    after coin 1: [1, 1, 1, 1, 1, 1]
    after coin 2: [1, 1, 2, 2, 3, 3]
    after coin 5: [1, 1, 2, 2, 3, 4]
    

    After the coin 1, each sum has one combination: all ones. The coin 2 adds the combinations that use 2s, so the sum 4 now has three (1+1+1+1, 2+1+1, 2+2). The coin 5 adds one more way to make 5, the single coin. The last entry is 4, the answer.

    For amount = 4 and coins = [1, 2, 3] the same printout gives:

    after coin 1: [1, 1, 1, 1, 1]
    after coin 2: [1, 1, 2, 2, 3]
    after coin 3: [1, 1, 2, 3, 4]
    

    Step by step

    1. Create ways as an int[] of size amount + 1. A new int array starts with zeros: before any coin is added, no sum above 0 can be made.
    2. Set ways[0] to 1. The sum 0 has exactly one combination, and every other combination grows from it.
    3. Loop over coins with an enhanced for loop. The coins are the outer loop, which is what makes the counts combinations.
    4. Loop a from coin up to amount. A sum below coin can't contain it, and going up lets a smaller sum already include this coin.
    5. Add ways[a - coin] to ways[a]. This is the key line.
    6. Return ways[amount]. When amount is 0 the loops don't run and this returns ways[0], which is 1.

    The solution in Java

    class Solution {
        public int change(int amount, int[] coins) {
            int[] ways = new int[amount + 1];
            ways[0] = 1;
            for (int coin : coins) {
                for (int a = coin; a <= amount; a++) {
                    ways[a] += ways[a - coin];
                }
            }
            return ways[amount];
        }
    }
    

    The Java details worth a closer look:

    • The enhanced for loop. for (int coin : coins) visits the array's values in order. It gives you a copy of each value, which is all this code needs.
    • Arrays start at zero. new int[amount + 1] fills every slot with 0, so only ways[0] needs setting. The size is amount + 1 because the indexes run from 0 to amount inclusive.
    • Compound assignment. ways[a] += ways[a - coin] reads both entries and writes the sum back. Because a counts upward, ways[a - coin] has already been updated for this coin.
    • int overflow. An int wraps silently past 2147483647. The lesson promises that the answer, and the number of combinations for every smaller amount, fits in an int, so no long is needed here. If you reuse the idea without that promise, switch the array to long[].

    Complexity

    Time: O(n · amount), where n is the number of coins. Each coin goes over at most amount + 1 sums, with constant work for each. With 300 coins and an amount of 5000, that is about 1.5 million additions.

    Space: O(amount). The array ways holds amount + 1 counts, and nothing else grows with the input.

    Edge cases and common mistakes

    • Amount 0. change(0, new int[]{7}) returns 1: the inner loop never runs and ways[0] is 1.
    • A coin bigger than the amount. change(3, new int[]{2}) is 0, and a coin of 10 with amount = 5 is skipped, because the loop starts above amount and doesn't run.
    • A coin equal to the amount. change(5, new int[]{5}) and change(10, new int[]{10}) both return 1.
    • Swapping the loops. Putting the sums outside and the coins inside counts orders. For amount = 4 and coins = [1, 2, 3] that version returns 7 instead of 4.
    • Looping downward. Going from amount down to coin would let each coin be used at most once, which is a different problem.
    • Starting ways[0] at 0. Every count would stay 0. The empty combination is what everything else is built from.

    Testing

    • amount = 5, coins = [1, 2, 5] gives 4.
    • amount = 3, coins = [2] gives 0.
    • amount = 4, coins = [1, 2, 3] gives 4.
    • amount = 0, coins = [7] gives 1.
    • amount = 10, coins = [10] gives 1.
    • amount = 5000, coins = [1, 2, 5] gives 1252001.
    Keep going with Java500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free