Coin Change II


Coin Change II – LeetCode


Problem Statement

Given an integer amount and an array coins of distinct integers representing coin denominations, return the number of combinations that make up that amount.
You may use unlimited coins of any denomination.


Intuition


Step 1: Recursion (Pick / Not Pick with IBH)

IBH Format

Java Code: Recursion

class Solution {
    public int change(int amount, int[] coins) {
        return count(coins.length - 1, amount, coins);
    }

    private int count(int i, int amount, int[] coins) {
        if (i == 0) {
            return (amount % coins[0] == 0) ? 1 : 0;
        }

        int notPick = count(i - 1, amount, coins);
        int pick = 0;
        if (coins[i] <= amount) {
            pick = count(i, amount - coins[i], coins);
        }

        return pick + notPick;
    }
}

Step 2: Memoization (Top-Down DP)

Java Code: Memoization

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        Integer[][] dp = new Integer[n][amount + 1];
        return count(n - 1, amount, coins, dp);
    }

    private int count(int i, int amount, int[] coins, Integer[][] dp) {
        if (i == 0) {
            return (amount % coins[0] == 0) ? 1 : 0;
        }

        if (dp[i][amount] != null) return dp[i][amount];

        int notPick = count(i - 1, amount, coins, dp);
        int pick = 0;
        if (coins[i] <= amount) {
            pick = count(i, amount - coins[i], coins, dp);
        }

        return dp[i][amount] = pick + notPick;
    }
}

Step 3: Tabulation (Bottom-Up DP)

Java Code

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        int[][] dp = new int[n][amount + 1];

        // Base case: only use coins[0]
        for (int amt = 0; amt <= amount; amt++) {
            dp[0][amt] = (amt % coins[0] == 0) ? 1 : 0;
        }

        for (int i = 1; i < n; i++) {
            for (int amt = 0; amt <= amount; amt++) {
                int notPick = dp[i - 1][amt];
                int pick = 0;
                if (coins[i] <= amt) {
                    pick = dp[i][amt - coins[i]];
                }
                dp[i][amt] = pick + notPick;
            }
        }

        return dp[n - 1][amount];
    }
}

Step 4: Space Optimization (2 Rows → 1 Row)

Java Code: Optimized

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        int[] prev = new int[amount + 1];

        // Base case
        for (int amt = 0; amt <= amount; amt++) {
            prev[amt] = (amt % coins[0] == 0) ? 1 : 0;
        }

        for (int i = 1; i < n; i++) {
            int[] curr = new int[amount + 1];
            for (int amt = 0; amt <= amount; amt++) {
                int notPick = prev[amt];
                int pick = 0;
                if (coins[i] <= amt) {
                    pick = curr[amt - coins[i]];
                }
                curr[amt] = pick + notPick;
            }
            prev = curr;
        }

        return prev[amount];
    }
}

Choice Diagram

                      count(i, amount)
                      /            \
             notPick                pick
        count(i - 1, amount)   count(i, amount - coins[i])

Final Notes