Coin Change

Medium Dynamic Programming Knapsack Original on LeetCode

Coin Change is a medium dynamic programming problem solved with the knapsack pattern. The best approach, optimal (unbounded knapsack dp), runs in O(amount · k) time and O(amount) space. Below are 2 approaches in Java, from recursion up.

Problem

Given coin values and a target amount, return the fewest coins needed to make exactly that amount, or -1 if it cannot be made. You have unlimited coins of each value.

Examples

Example 1

Input
coins = [1, 2, 5], amount = 11
Output
3
Why
5 + 5 + 1.

Example 2

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

Constraints

  • 1 <= coins.length <= 12; 0 <= amount <= 10^4.
  • Unlimited coins of each type.

Animated walkthrough

A narrated, step-by-step animation that builds the solution from the idea up. Press play, or step through it at your own pace.

Concepts first, then the problem and every approach, step by step.

Space to play or pause · ← → to jump a step · click or drag the bar to seek

Solutions

Try it yourself first. Then compare: each approach lists its idea, the steps, its time and space, and the Java code.

ApproachTimeSpace
RecursionO(k^(amount / min coin))O(amount)
Optimal (unbounded knapsack DP)O(amount · k)O(amount)

1Recursion

TimeO(k^(amount / min coin))
SpaceO(amount)

Try every coin as the last one and recurse on the remaining amount.

  1. f(0) = 0; f(a < 0) = infinity; f(a) = 1 + min f(a - c).
Java
class Solution {
    public int coinChange(int[] coins, int amount) {
        int r = f(coins, amount);
        return r == Integer.MAX_VALUE ? -1 : r;
    }

    private int f(int[] coins, int a) {
        if (a == 0) return 0;
        if (a < 0) return Integer.MAX_VALUE;
        int best = Integer.MAX_VALUE;
        for (int c : coins) {
            int sub = f(coins, a - c);
            if (sub != Integer.MAX_VALUE) best = Math.min(best, sub + 1);
        }
        return best;
    }
}

2Optimal (unbounded knapsack DP)

TimeO(amount · k)
SpaceO(amount)

Build dp from 0 up to amount. Each amount takes the best of using each coin last. Unreachable amounts stay at a sentinel larger than any real answer.

  1. dp[0] = 0, dp[1..amount] = amount + 1.
  2. For a in 1..amount, for c in coins with c <= a: dp[a] = min(dp[a], dp[a - c] + 1).
  3. Return dp[amount] > amount ? -1 : dp[amount].
Java
class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;
        for (int a = 1; a <= amount; a++)
            for (int c : coins)
                if (c <= a) dp[a] = Math.min(dp[a], dp[a - c] + 1);
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

Edge cases to test

  • amount = 0 (answer 0)
  • Impossible amounts
  • Greedy fails, e.g. coins [1, 3, 4] for amount 6 (3 + 3 beats 4 + 1 + 1)

Hints

Hint 1

dp[a] = fewest coins that make amount a = 1 + min over coins c of dp[a - c].

FAQ

What is the best time complexity for Coin Change?

Optimal (unbounded knapsack DP) runs in O(amount · k) time and O(amount) extra space.

Which pattern does Coin Change use?

It is a dynamic programming problem that uses the knapsack pattern. Other problems with the same pattern: 0 - 1 Knapsack Problem, Fractional Knapsack, Knapsack with Duplicate Items.

Is there a brute force solution for Coin Change?

Yes. Recursion takes O(k^(amount / min coin)) time and O(amount) space. Try every coin as the last one and recurse on the remaining amount.

Which edge cases should I test for Coin Change?

amount = 0 (answer 0); Impossible amounts; Greedy fails, e.g. coins [1, 3, 4] for amount 6 (3 + 3 beats 4 + 1 + 1).