Skip to content
FoundationVerified ImplementationLive Benchmarkable

Coin Change (Minimum Coins DP)

Computes the minimum number of coins from a given denomination set needed to make an exact target amount using bottom-up 1D dynamic programming.

Quick Facts

Compact metadata for implementation and discovery.

Category
Optimization
Difficulty
Beginner
Family
Dynamic Programming
Tier
Foundation
Status
Verified
Review
Source Checked
Sources
1
Formulas
1
Relationships
1
Code example
Ready
Visualization
Ready
Aliases
Change-Making Problem, Minimum Coin Change

Overview

Unlike greedy coin selection—which fails on non-canonical denominations such as [1, 3, 4] for target 6—bottom-up Coin Change DP evaluates every reachable sub-amount from 1 to target in O(amount * |coins|) time, guaranteeing the globally minimum coin count.

Core Concepts

unbounded knapsack recurrencebottom-up tabulationnon-canonical denominations

Quick facts

Complexity

Runtime and memory behavior at a glance.

Best

O(amount * k)

Average

O(amount * k)

Worst

O(amount * k)

Space

O(amount)

Interactive Runtime & Memory Budget Calculator

Estimate wall-clock latency, operation count, and working memory for Coin Change (Minimum Coins DP) across input scales.

Input Scale N:
JIT-compiled JS/TS (~350M primitive ops/sec)
Est. Average TimeO(amount * k)

428.6 µs

60 FPS Frame-Safe (<16ms)~150.0k ops
Est. Worst-Case TimeO(amount * k)

428.6 µs

60 FPS Frame-Safe (<16ms)~150.0k ops
Est. Working MemoryO(amount)

1.53 MB

Standard Heap FitN=100,000

When to Use

  • Dynamic programming minimum-coin optimization computing the fewest coins needed to sum to a target amount across arbitrary coin denominations.
  • Use Coin Change DP when denominations are arbitrary and greedy largest-first selection can be suboptimal.

When Not to Use

  • Avoid O(amount) tabulation when amount is huge (e.g., >10^8) and canonical currency rules allow O(k) division.

What It Does

  • Builds a 1D table dp[0..amount] where dp[s] is the fewest coins needed to sum to s
  • Transitions via dp[s] = min(dp[s], dp[s - coin] + 1) for each denomination

Use Cases

  • Making exact change or parcel weight packing with non-canonical denominations
  • Unbounded knapsack resource allocation with unit item costs

Inputs

  • Array of positive coin denominations and non-negative integer target amount

Outputs

  • Minimum number of coins required, or -1 if unreachable

Key Formulas (1)

Bellman Recurrence

dp[s] = 1 + \min_{c \in C,\, c \le s} dp[s − c]
dp[s] = 1 + \min_{c \in C,\, c \le s} dp[s - c]

Minimum coins to form sum s.

Pseudocode

  1. 1.Allocate dp[0..amount] filled with amount + 1, and set dp[0] = 0
  2. 2.For each coin in coins:
  3. 3. For s = coin to amount:
  4. 4. dp[s] = min(dp[s], dp[s - coin] + 1)
  5. 5.Return dp[amount] if dp[amount] <= amount else -1

Verified Implementation & Unit Tests

Verified Implementation

Tested TypeScript and Python 3 implementations paired with unit tests covering edge cases, duplicate keys, and boundary conditions.

/**
 * Coin Change Minimum Coins — Bottom-up 1D unbounded knapsack DP
 * Time: O(amount * |coins|) | Space: O(amount)
 */
export function coinChangeMin(coins: readonly number[], amount: number): number {
  if (amount === 0) return 0;
  const dp = new Int32Array(amount + 1).fill(amount + 1);
  dp[0] = 0;
  for (let a = 1; a <= amount; a++) {
    for (const c of coins) {
      if (c <= a && dp[a - c] + 1 < dp[a]) {
        dp[a] = dp[a - c] + 1;
      }
    }
  }
  return dp[amount] > amount ? -1 : dp[amount];
}

Canonical Example

Input
coins = [1, 3, 4], amount = 6
Output
2 (3 + 3)
Explanation
DP discovers dp[6] = dp[3] + 1 = 2, beating the greedy 4 + 1 + 1 = 3 solution.

Implementation Notes

  • Initialize unreachable states to amount + 1 rather than Infinity so integer addition dp[s - c] + 1 never overflows.

Common Pitfalls

  • Using greedy largest-coin-first on denominations like [1, 3, 4] for amount 6 (greedy picks 4+1+1 = 3 coins instead of 3+3 = 2 coins).

Interactive Mechanism Walkthrough & State Debugger

Array Partition & Ordering Trace

1D state array filling left-to-right as each coin denomination relaxes reachable sums.

38
[0]
14
[1]
52
[2]
23
[3]
61
[4]
19
[5]
44
[6]
29
[7]
step1 / 5
cursor i0 (val=38)
pivot / key61
sorted prefix0 / 8 items
invariantPartitioning prefix
Step 1 of 5: Allocate dp[0..amount] filled with amount + 1, and set dp[0] = 0
array-sort

Notes

Demonstrates why dynamic programming is required when the greedy matroid property does not hold.

Sources (1)

What each reference supports for this entry.

Introduction to Algorithms (CLRS)

Book

Dynamic programming formulation of the change-making problem.

Open source

Explore Next

Good next jumps from the relationship graph, ranked for useful discovery.

Unbounded forward capacity sweep vs 0/1 reverse capacity sweep.

Tags

Dynamic ProgrammingCombinatorial OptimizationKnapsack