🚀 Phases 1–5 are live — Days 1–17 cover the foundations and the algorithmic patterns. See the roadmap →

Coin Change medium

Description

You’re given coin denominations coins[] and a target amount. Return the fewest number of coins that sum to amount. Each coin can be used an unlimited number of times. If the amount can’t be made, return -1.

Examples

> Case 1:
    coins = [1, 2, 5],  amount = 11
    Output: 3
    Explanation: 11 = 5 + 5 + 1.
 
> Case 2:
    coins = [2],  amount = 3
    Output: -1
 
> Case 3:
    coins = [1],  amount = 0
    Output: 0

Constraints

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 2^31 - 1
  • 0 <= amount <= 10^4

State design

This is unbounded knapsack in its purest form — pick coins (items) with repetition, minimize count, subject to a fixed sum.

  1. What am I computing? dp[w] = min coins to make amount w.
  2. Dimensions? One — w.
  3. Base cases? dp[0] = 0 (zero coins for zero amount). Mark all other dp[w] as infinity / unreachable.
  4. Transition? For each amount w, try every coin c <= w: dp[w] = min(dp[w], dp[w - c] + 1).
  5. Order? Amount w from 1 to amount. (Each dp[w] reads dp[w - c] for smaller w.)
  6. Answer? dp[amount], or -1 if it stayed at infinity.
⚠️

The greedy “always take the largest coin first” doesn’t work in general. For coins = [1, 3, 4], amount = 6: greedy gives 4 + 1 + 1 = 3 coins, but optimal is 3 + 3 = 2 coins. DP is required. For specific currency systems (US dollars, euros) greedy happens to be optimal — but never assume it.

Code

Why use amount + 1 as infinity instead of INT_MAX? Because dp[w - c] + 1 might overflow if you start from INT_MAX. And no legitimate answer can exceed amount (the worst case is using amount ones), so amount + 1 is a safe sentinel.

Analysis

  • Time: O(amount × len(coins)).
  • Space: O(amount).

Count the number of distinct ways to make amount using unlimited coins.

Same state, different operator, and a different loop order — to count combinations (unordered), iterate coins in the outer loop:

def change(amount, coins):
    dp = [0] * (amount + 1)
    dp[0] = 1
    for c in coins:                       # coins outside
        for w in range(c, amount + 1):     # amount inside
            dp[w] += dp[w - c]
    return dp[amount]

Swap the loops and you’d count permutations (ordered) — a different problem.