Description

Coin Change
You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

You may assume that you have an infinite number of each kind of coin.

Example 1:
Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:
Input: coins = [2], amount = 3
Output: -1

Example 3:
Input: coins = [1], amount = 0
Output: 0

Constraints:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 231 - 1
  • 0 <= amount <= 104

Approach

  • Bottom Up -> Check solution for the amount by subtracting the coin and add one
  • Initialize array with 1 index and value amount + 1 so it can be used later too to return -1
  • In simplest terms we d[i] will tell us the coins required to reach the i amount we fill all with amount + 1 to show that it is impossible to reach and we fill 0 index with zero saying it takes 0 coin to reach 0 amount
  • The outer loop iterates over each possible amount from 1 to amount. For each amount i, we try to figure out how to make i using the available coin denominations.
  • With inner loop we will check what was the solution if we remove the j coin and add one coin which is j, min condition is because initially we assigned a higher value to each index
  • Time: O(n*t) Space: O(t)
  • Where n is the length of the array coins and t is the given amount.
class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] d = new int[amount + 1];
        Arrays.fill(d,amount + 1);
        d[0] = 0;
        for (int i = 1; i <= amount; i++){
            for (int j = 0; j < coins.length; j++) {
                if (coins[j] <= i) {
                    d[i] = Math.min(d[i], d[i - coins[j]] + 1);
                }
            }
        }
        return d[amount] > amount ? -1 : d[amount];
    }
}

Brute Force Solution (Recursive / Top-Down DFS)

The intuitive brute-force approach explores all possible ways to form the target amount by trying every coin at each step.

  • Idea: To make up an amount, try subtracting each coin denomination from amount. The problem then reduces to finding the minimum coins needed for the remaining amount and adding for the coin used.
  • Base Cases:
  • If amount == 0, 0 coins are needed.
  • If amount < 0, it’s impossible (return infinity / impossible flag).
class Solution {
    public int coinChange(int[] coins, int amount) {
        int ans = helper(coins, amount);
        return ans == Integer.MAX_VALUE ? -1 : ans;
    }
 
    private int helper(int[] coins, int rem) {
        if (rem == 0) return 0;
        if (rem < 0) return Integer.MAX_VALUE;
 
        int minCoins = Integer.MAX_VALUE;
 
        for (int coin : coins) {
            int res = helper(coins, rem - coin);
            if (res != Integer.MAX_VALUE) {
                minCoins = Math.min(minCoins, res + 1);
            }
        }
 
        return minCoins;
    }
}
 
  • Complexity:
  • Time: where is amount and is coins.length (exponential due to recomputing the same amounts repeatedly).
  • Space: recursion stack depth.

Most Optimized Solution (Bottom-Up Dynamic Programming)

The solution already written in your editor is the most optimal and standard standard dynamic programming approach.

Why it’s optimal and intuitive to remember:

  1. Core Concept: dp[i] represents the minimum number of coins needed to make amount i.
  2. Transition: To find dp[i], try taking every coin where . The cost would be 1 + dp[i - c]. We take the minimum across all possible coins.
  3. Initialization: Fill dp with amount + 1 (a sentinel value acting as infinity, since you can never need more than amount coins if min coin is 1). Set dp[0] = 0.
class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0; // 0 coins needed to make amount 0
 
        for (int i = 1; i <= amount; i++) {
            for (int coin : coins) {
                if (coin <= i) {
                    dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                }
            }
        }
 
        return dp[amount] > amount ? -1 : dp[amount];
    }
}
 
  • Complexity:
  • Time: where is coins.length and is amount.
  • Space: for the 1D DP table.