Coin Change Solution
Given coin denominations and a target amount, return the fewest coins needed to make that amount. Return -1 if impossible.
Problem Statement
You have an infinite supply of coins of given denominations. Return the minimum number of coins to make up amount. If no combination works, return -1.
Greedy / Recursion — incorrect or O(Sⁿ)
Greedy: always use the largest coin first. This fails — e.g., coins=[1,3,4], amount=6: greedy picks 4+1+1=3 coins, but 3+3=2 is optimal. Exhaustive recursion tries all combinations but is exponential.
Greedy assumes the largest coin always helps, but sometimes skipping a large coin leads to a better combination. Only DP or BFS over all possible remaining amounts guarantees the minimum.
minCoins(amount=3) is recomputed from multiple paths. Bottom-up DP fills a 1D array from 0 to amount, each cell O(coins) work, total O(amount × coins).
| Metric | Value |
|---|---|
| Time | O(Sⁿ) where S = amount/min_coin |
| Space | O(amount) call stack |
Bottom-Up DP — O(amount × coins)
Build array dp[0..amount] where dp[a] = minimum coins for amount a. For each amount from 1 to target, try each coin: dp[a] = min(dp[a], dp[a - coin] + 1).
- Create
dp[amount+1], fill withamount+1(impossible sentinel — any valid count ≤ amount). dp[0] = 0.- For a from 1 to amount: for each coin, if
coin ≤ a:dp[a] = min(dp[a], dp[a-coin] + 1). - Return
dp[amount]if< amount+1, else-1.
- "Minimum items from unlimited supply to reach a target" = unbounded knapsack.
- The signal: each item can be used multiple times, and you minimize (or count) combinations.
- Also in LC 518 (Coin Change 2 — count combinations), LC 279 (Perfect Squares — treat squares as "coins").
amount+1 as sentinel instead of Integer.MAX_VALUE:
- Using MAX_VALUE risks overflow when computing
dp[a-coin] + 1. - Since no valid answer can exceed
amount(worst case: all 1-cent coins),amount+1is a safe, overflow-free sentinel.
Visual Walkthrough
Trace for coins = [1, 3, 4], amount = 6.
Code — Java & Python
Complexity Analysis
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Greedy | O(amount/min) | O(1) | Wrong answer — greedy doesn't guarantee minimum |
| Recursive (brute force) | O(Sⁿ) | O(amount) | Correct but exponential |
| Bottom-up DP ← optimal | O(amount × n) | O(amount) | n = number of coin types; fills each cell once |
Edge Cases & Pitfalls
| Case | Input | Why It Matters |
|---|---|---|
| amount = 0 | coins=[1], amount=0 | dp[0]=0. Return 0 — no coins needed. |
| Impossible | coins=[2], amount=3 | dp[3] stays at sentinel. Return -1. |
| Single coin = 1 | coins=[1], amount=5 | Answer is always amount itself (5 pennies). |
| Exact match | coins=[5], amount=5 | dp[5] = dp[0]+1 = 1. |
| Large amount | amount=10000 | dp array of 10001 ints. O(10000 × 12) = ~120K operations. Fine. |
Integer.MAX_VALUE as the sentinel and then computing dp[a-c] + 1 — this overflows to Integer.MIN_VALUE, which becomes the "minimum" and corrupts the dp array. Always use amount + 1 as the sentinel since no valid answer exceeds amount.