518. Coin Change 2
At a Glance
- Topic: dp-2d
- Pattern: Dynamic Programming (unbounded knapsack: count ways)
- Difficulty: Medium
- Companies: Google, Facebook, Microsoft, Amazon, Apple
- Frequency: High
- LeetCode: 518
Problem (one-liner)
Given amount and distinct positive coins (infinite use each), return the number of combinations that sum to amount. Order of coins in a combination does not matter (1+2 and 2+1 count once). Input: amount, coins. Output: count.
Recognition Cues
- “Number of combinations” to make amount
- “Order does not matter” → process coins in fixed outer loop
- Unbounded use → inner loop on sums ascending
Diagram
At-a-glance flow (replace with problem-specific Mermaid as you refine this note). camelCase node IDs; no spaces in IDs.
Loading diagram…
Approaches
- Brute force — enumerate integer partitions — exponential.
- Better — 2D
ways[coinIndex][sum]—O(coins * amount)time. - Optimal — 1D
ways[sum]with coin-outer loop —O(coins * amount)time /O(amount)space. <- pick this in interview.
Optimal Solution
Go
package main
func changeTable(amount int, coins []int) int {
ways := make([][]int, len(coins)+1)
for index := range ways {
ways[index] = make([]int, amount+1)
ways[index][0] = 1
}
for coinIndex := 1; coinIndex <= len(coins); coinIndex++ {
coin := coins[coinIndex-1]
for sum := 0; sum <= amount; sum++ {
without := ways[coinIndex-1][sum]
with := 0
if sum >= coin {
with = ways[coinIndex][sum-coin]
}
ways[coinIndex][sum] = without + with
}
}
return ways[len(coins)][amount]
}
func change(amount int, coins []int) int {
ways := make([]int, amount+1)
ways[0] = 1
for _, coin := range coins {
for sum := coin; sum <= amount; sum++ {
ways[sum] += ways[sum-coin]
}
}
return ways[amount]
}JavaScript
function changeTable(amount, coins) {
const numCoins = coins.length;
const ways = Array.from({ length: numCoins + 1 }, () =>
new Array(amount + 1).fill(0)
);
for (let index = 0; index <= numCoins; index++) {
ways[index][0] = 1;
}
for (let coinIndex = 1; coinIndex <= numCoins; coinIndex++) {
const coin = coins[coinIndex - 1];
for (let sum = 0; sum <= amount; sum++) {
const without = ways[coinIndex - 1][sum];
const withCoin = sum >= coin ? ways[coinIndex][sum - coin] : 0;
ways[coinIndex][sum] = without + withCoin;
}
}
return ways[numCoins][amount];
}
function change(amount, coins) {
const ways = new Array(amount + 1).fill(0);
ways[0] = 1;
for (const coin of coins) {
for (let sum = coin; sum <= amount; sum++) {
ways[sum] += ways[sum - coin];
}
}
return ways[amount];
}Walkthrough
amount = 5, coins = [1,2,5]
1D after each coin (conceptually):
- start:
[1,0,0,0,0,0] - add
1: all sums reachable - add
2: ways multiply/combine... Finalways[5]counts only order-agnostic combinations.
Invariant: processing coins in a fixed order counts each multiset exactly once.
Edge Cases
amount = 0→1(empty combination)- Coin larger than
amount→ may still combine smaller coins
Pitfalls
- Inner loop
sumdescending → becomes 0/1 knapsack (wrong) - Sum outer, coin inner → overcounts permutations
Similar Problems
- 322. Coin Change — minimum coins (same loops, min vs sum).
- 494. Target Sum — sign assignments; related subset counts.
Variants
- Limit multiplicity per coin → bounded knapsack via binary splitting.
Mind-Map Tags
#unbounded-knapsack #combinations #coin-change-2 #medium
Last updated on
Spotted something unclear or wrong on this page?