CMD Guide
HomeDSADynamic Programming

Coin Change

Coin Change — Counting Combinations (Unbounded Knapsack)

The number of ways to make amount T from unlimited-supply coins is built up by deciding, one coin denomination at a time, how many copies of that coin to use — each decision either reuses a smaller amount's already-computed answer (dp[j-coin], allowing the same coin again) or leaves the count untouched (skip the coin), so the table fills as a running accumulation rather than a fresh choice per amount.

Recognize the pattern

Brute force → optimal

Brute force: recursion — for each coin, branch into "use it (amount shrinks, coin stays available)" vs "move to next coin". Recurrence: ways(i, amt) = ways(i, amt-coins[i]) + ways(i+1, amt), base cases amt==0 → 1, amt<0 or i==n → 0. A single low-value coin does not cause blow-up: with coins={1}, amount=T, the "skip" branch hits i==n and returns 0 immediately at every level, so ways(0,T) makes only ~2T+1 calls total — linear, not exponential. Real blow-up needs many coins with count comparable to T, because then both the "use" and "skip" branches stay alive for many levels simultaneously. Concretely: coins = T copies of denomination 1 (n=T coin slots), amount=T. The recurrence ways(i,amt)=ways(i,amt-1)+ways(i+1,amt) then matches the recurrence for counting monotone lattice paths from (0,T) to the boundary, and the call count grows like C(2T,T) ≈ 4^T/√T — exponential in T, but shaped by combinatorial path-counting, not a clean 2^T.

Optimal: memoize (i, amt) → O(n·T) distinct states, O(1) work each, giving O(n·T) time. Because row i only reads row i (same coin, smaller amount) and row i-1, the 2D table collapses to a single 1D array of size T+1, updated in place per coin.

Complexity, derived

Let n = number of coins, T = target amount. The DP conceptually fills an (n+1)×(T+1) grid; the 1D-collapsed version is one array of size T+1 refreshed n times. Total cell visits = n × (T+1), each visit doing one addition and one bound check — O(1) work → time = Θ(n·T). Space: only the current 1D array is kept → Θ(T), versus Θ(n·T) for the full 2D table (needed only if you must reconstruct which coins were used, not just count). This is the payoff of memoization: the unmemoized recursion above can reach Θ(C(2T,T)) calls when n is comparable to T, while memoizing collapses it to the Θ(n·T) states that actually exist — a real, demonstrated blow-up, not an asserted one.

Worked example

coins = {1,2,3}, amount = 5. dp[j] = ways to make j using coins processed so far. Init dp = [1,0,0,0,0,0] (dp[0]=1: one way to make 0, use no coins).

After coindp[0]dp[1]dp[2]dp[3]dp[4]dp[5]
coin=1 (dp[j]+=dp[j-1], j=1..5)111111
coin=2 (dp[j]+=dp[j-2], j=2..5)112233
coin=3 (dp[j]+=dp[j-3], j=3..5)112345

Final dp[5] = 5 — matches the five combinations {1,1,1,1,1}, {1,1,1,2}, {1,2,2}, {1,1,3}, {2,3}.

Java — combinations (order doesn't matter)

int change(int amount, int[] coins) {
    int[] dp = new int[amount + 1];
    dp[0] = 1;
    for (int coin : coins) {              // outer: coin
        for (int j = coin; j <= amount; j++) { // inner: amount, ascending
            dp[j] += dp[j - coin];
        }
    }
    return dp[amount];
}

Pitfalls

When to use / when NOT — trade-offs

Use this 1D unbounded-DP pattern whenever coins are reusable and you need a count or optimum over amounts up to T with T not too large (state space n·T must be tractable, e.g. T ≤ 10^4 as here).

Takeaways

Recall: Why does processing coins in the outer loop (rather than amounts in the outer loop) guarantee each combination is counted exactly once, not once per ordering?


Compiled from LeetCode 518 (Coin Change II) problem framing and standard unbounded-knapsack DP derivations; worked example, the brute-force blow-up example (re-derived to actually exhibit exponential growth), complexity analysis, and diagrams are original to this page.

🤖 Don't fully get this? Learn it with Claude

Stuck on Coin Change? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Coin Change** (DSA) and want to truly understand it. Explain Coin Change from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Coin Change** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Coin Change** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Coin Change** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes