Coin Change
Problem statement
You have coins of given denominations, unlimited of each. Return the fewest coins that sum to amount. If nothing works, return -1.
Example:
Input:
coins = [1, 2, 5], amount = 11
Expected output:
3
Why: 5 + 5 + 1. Four 2s and three 1s also make 11, but they use more coins.
A miss: coins = [2], amount = 3 → -1.
Practice on LeetCode: Coin Change
What we are asking
The question is not “can I make this amount.” It is “what is the shortest list of coins that does.”
dfs(target) is the fewest coins that make target. You try every denomination once as the next coin:
dfs(target) = 1 + min(dfs(target - coin) for each coin)
dfs(0) = 0
dfs(negative) = impossible
On 11 with {1, 2, 5}:
dfs(11) = 1 + min(dfs(10), dfs(9), dfs(6))
dfs(6) = 1 + min(dfs(5), dfs(4), dfs(1))
dfs(5) = 1 // one 5
That path is 5, then 5, then 1 — three coins. Other branches are longer and lose the min.
Why greedy is wrong
Biggest coin first looks right on [1, 2, 5]. It is not a rule.
coins = [1, 3, 4], amount = 6: greedy takes 4 + 1 + 1 (three coins). 3 + 3 is two. The large coin steals the pairing that would have been cheaper.
So you have to try every next coin. That tree shares remainders — dfs(6) shows up under both 11 - 5 and other prefixes. There are only amount + 1 remainders. Remember each one.
Bottom-up is the same recurrence in an array: dp[x] = fewest coins for x, start dp[0] = 0, relax dp[x] = min(dp[x], dp[x - coin] + 1).
Golang Solution
A map from remaining amount to fewest coins. math.MaxInt is “impossible.” Skip that child so res+1 does not overflow. If the top call is still MaxInt, return -1.
Time: O(amount · k) — k is the number of denominations
Space: O(amount) — memo, plus recursion depth in the worst case
import "math"
func coinChange(coins []int, amount int) int {
memo := map[int]int{}
var dfs func(int) int
dfs = func(target int) int {
if target == 0 {
return 0
}
if target < 0 {
return math.MaxInt
}
if v, ok := memo[target]; ok {
return v
}
ans := math.MaxInt
for _, coin := range coins {
res := dfs(target - coin)
if res != math.MaxInt {
ans = min(ans, res+1)
}
}
memo[target] = ans
return ans
}
ans := dfs(amount)
if ans == math.MaxInt {
return -1
}
return ans
}
amount == 0 is 0 coins — you are already done. Impossible remainders are stored too (MaxInt), so a dead amount is not searched twice.
