Coin Change & Unbounded Knapsack
The previous lesson let each item be taken at most once. Drop that cap — items may be reused any number of times — and the 0/1 knapsack collapses from a two-dimensional table to a one-dimensional one, because there is no longer a prefix of "already-used" items to track.
╌╌╌╌
The previous lesson solved 0/1 knapsack, where each item is taken whole or left
behind, at most once. The 0/1
in the name was the include/exclude bit on
every item, and it forced a two-dimensional table : we had to remember
which prefix of items was still on the table, because once item was used it
could not be used again. Now relax exactly that constraint. Let every item be
available in unlimited supply, so the thief may pack as many copies of item
as fit. This is the unbounded knapsack problem, and allowing
infinite copies removes one whole dimension from the
dynamic program's table.
In 0/1 knapsack the second index existed to stop us from reusing an item; whether item had already been taken was genuine state. When items may be reused without limit, that question is meaningless (the set of available items never shrinks), so the only thing a subproblem needs to remember is how much capacity is left. One number, one dimension.
Unbounded knapsack: one dimension instead of two
Define the subproblem on capacity alone:
The answer is (or if we want at most
; padding
with a zero-value, weight-one item makes them equal). To fill , consider the
last item placed into the knapsack. It is some type with ; after
placing it we have value plus the best we can do with the remaining budget
, and that remaining budget may itself use type again:
Compare the right-hand side to 0/1 knapsack's
. There the include branch read
, the previous row, item removed from the pool. Here the
include branch reads , the same , item still in the pool.
That one difference is the whole distinction between use once
and use any number of times.
The figure below folds one item of weight into a single array and contrasts the two sweep directions. Ascending, cell reads after that cell was already touched this pass, so an item can chain into itself (reuse). Descending, reads while it still holds the pre-pass value, so each item lands at most once.
Because the available-item set never changes, the item loop and the weight loop
may be nested in either order; there is no previous row
to respect, only the
ascending-weight rule. We fill cells, each scanning up to items:
time, the same as 0/1 knapsack, but in space: one array, no second dimension to collapse.
Coin change — minimum coins
The cleanest instance of unbounded knapsack strips the values away, just as subset-sum stripped them from 0/1 knapsack. Fix a set of coin denominations , available in unlimited supply, and an amount . Ask: what is the fewest coins that sum to exactly ?
This is unbounded knapsack with every item's value set to (one coin) and the objective flipped to minimize: we want the smallest count, not the largest value. Let be the minimum number of coins summing to amount :
The pays for the coin we just placed; the over denominations picks the best amount to reach the shortfall . An amount that no combination of coins can hit keeps the sentinel value , which propagates: if every is then so is . The figure below shows the 1-D table filling left to right, each cell reaching back positions for each denomination.
A full table, cell by cell. Take coins and fill left to right. Each cell tries all three denominations and keeps the smallest ; the winning coin is recorded in for reconstruction.
- — empty pile, no coins. undefined.
- ; only coin fits. .
- ; again only coin fits. .
- ; coin wins over three ones. .
- ; coin wins. .
- ; coins or tie, e.g. . .
- ; coin wins, landing on . .
The finished tables:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 1 | 1 | 2 | 2 | |
| – | 1 | 1 | 3 | 4 | 4 | 3 |
So , achieved by : two coins, not the three a careless greedy pick () would take. The next figure fills the same array with every winning transition drawn in, so the whole computation is visible at once.
The row under the array records the winning coin at each amount. The two blue arrows are the traceback for amount : from follow to , then to — coin twice, the two shaded cells.
- 1
- 2for to do
- 3unreachable so far
- 4
- 5for to do
- 6if and then
- 7
- 8coin that closed the gap
- 9return: unreachable
The outer loop runs over amounts and the inner over coins, so the running time is in space, pseudo-polynomial in the sense of the previous lesson: polynomial in the numeric value but exponential in its bit length.1
Reconstruction. The value is the coin count; the coins themselves come from the array. Starting at , the denomination is the last coin used, so emit it and jump to ; repeat until .
- 1;
- 2while do
- 3coin that closed
- 4
- 5return
Coin change — counting combinations
A different question on the same coins: not how few coins, but how many distinct ways to make amount . This is Coin Change II, and it counts multisets of coins. and are two ways to make , but and are the same way, because a multiset has no order. Let be the number of such combinations summing to , with (the empty multiset is the one way to make ).
The naive recurrence "" is wrong for combinations; it counts and separately. The fix is structural and is the most famous loop-order subtlety in dynamic programming.
Coins-outer avoids the double count because each denomination is introduced exactly once and never revisited: every multiset is built in a fixed canonical order of denominations (coin first, then , and so on), so each multiset is reached by exactly one path through the loops. The amount-outer loop, by contrast, considers every coin as the possible last coin at every amount, so the same multiset is counted once for each ordering of its coins.
Because coin is introduced only after coin is fully folded in, each
multiset is built in the fixed order ones first, then twos,
so counts
and once apiece; a ordering is never generated.
- 1;
- 2for to docoins outer: combinations
- 3for to doamount inner, ascending: reuse
- 4
- 5return
Swapping the two loops (for a outside, for i inside) computes
instead, the ordered count (Combination
Sum IV), where and are distinct. Same body, same time;
the nesting alone flips the meaning.
A ways table, one coin at a time. Coins , amount . Start with (only the empty bag makes ), then fold in each coin, updating for ascending from to .
| after folding | ||||||
|---|---|---|---|---|---|---|
| start | 1 | 0 | 0 | 0 | 0 | 0 |
| coin | 1 | 1 | 1 | 1 | 1 | 1 |
| coin | 1 | 1 | 2 | 2 | 3 | 3 |
| coin | 1 | 1 | 2 | 2 | 3 | 4 |
After coin every amount has a single all-ones bag. Folding in coin adds, for each , the ways that end with (i.e. include) a , so becomes : , , . Folding in coin touches only , adding the single bag : , namely , , , and . Because each coin is folded in exactly once, no bag is ever counted under two orderings.
A worked count: combinations vs sequences
Take coins and amount . As an unordered count the answers are and : two combinations. As an ordered count we also distinguish the arrangements of , giving , , and , for three sequences. The figure traces both, and ties each to its loop order.
The blue link shows the discrepancy: the single combination on the left corresponds to the two ordered sequences and on the right. Counting the left column is the coins-outer loop; counting the right is amount-outer. Picking the wrong nesting silently computes the wrong quantity — no error, just a wrong number — which is why it is the classic bug.
Why greedy fails — and when it works
Coin change has a natural greedy heuristic: repeatedly take the largest coin that fits. For the U.S. currency system it always gives the minimum, which is why cashiers can make change without dynamic programming.
Largest-coin-first leaves a remainder () that the denominations cover badly, while a less greedy first step () leaves a remainder the coins cover perfectly.
A coin system is called canonical when the greedy algorithm is optimal for every amount; standard currencies (like ) are deliberately designed to be canonical so that greedy change-making works. Whether an arbitrary system is canonical is itself a nontrivial question (it can be decided by checking greedy against the DP optimum over a bounded range of amounts), but the safe default for an unknown denomination set is the dynamic program, which is correct for any coins.3
The same shape elsewhere: Perfect Squares and Word Break
Coin change is unbounded knapsack, and two well-known problems are the identical recurrence with the coins renamed.
Perfect Squares asks for the fewest perfect squares ()
summing to . That is all over again, with the coins
being the
squares :
The squares are reusable (you may use four times to make ), so it is the ascending-weight minimization we already wrote; only the denomination set changes.
Word Break asks whether a string can be segmented into dictionary words. The
coins
are now words, the amount
is a string prefix, and the table is indexed
by prefix length. Let be true if the first characters of split into
dictionary words:
It is the boolean () flavor, like subset-sum was to knapsack, where a word
ending at position plays the role of a coin of value
landing the
prefix on the earlier boundary . The empty prefix is the
always-reachable base, exactly like .
Canonical systems, Frobenius, and generating functions
The claim that greedy works on canonical systems
rests on a subtle
theory. Deciding whether an arbitrary -coin system is canonical was open for years;
Pearson (2005) gave an test, and Kozen and Zaks (1994) showed the smallest
counterexample — the least amount where greedy fails — always lies below
(the sum of the two largest coins), so a canonical system can be
certified by checking greedy against the DP only up to that bound. The everyday
is canonical by design, but even small tweaks break it: the once-real
British pre-decimal system and hypothetical sets like are not, which is
exactly why a cash register that must handle arbitrary denominations falls back to
the DP.
Coin change also touches classical number theory. The Frobenius problem — given coprime denominations, what is the largest amount that cannot be made at all? — asks which cells of the DP table stay . For two coins the answer is the closed form (the Frobenius number, or Chicken McNugget number), but for three or more coins no closed form is known and computing it is NP-hard in general (Ramírez Alfonsín, 1996). The reachable set (which amounts have ) is eventually periodic with period , a structure the DP table exhibits numerically.
The counting variant connects to generating functions from partition theory. The number of ways to make amount with coins is the coefficient of in , and the coins-outer DP loop is precisely the term-by-term multiplication of these geometric series — folding in one factor per coin. When the coins are all positive integers , this product is Euler's partition generating function, and the DP becomes a way to compute the partition numbers (the subject of Hardy and Ramanujan's famous asymptotic ). The Word Break instance, meanwhile, is the recognition problem for a language over a finite dictionary, and its natural generalization — count or weight the segmentations — reproduces the forward algorithm of a weighted finite-state model, which underlies tokenization and word segmentation.4
Takeaways
- Unbounded knapsack lets each item be used any number of times. That single change deletes the item dimension: the subproblem depends only on remaining capacity, giving the 1-D recurrence in time and space, versus 0/1 knapsack's 2-D .
- The include branch reads at the same item-availability (not the previous row), so we sweep weight ascending to permit reuse, the exact opposite of 0/1's descending sweep, which forbids it.
- Coin change (min coins) is unbounded knapsack with unit values and a objective: , , if unreachable; a array reconstructs the coins.
- Counting ways is governed by loop order: coins outer, amount inner counts unordered combinations (Coin Change II); amount outer, coins inner counts ordered sequences (Combination Sum IV). Same code, different question: the classic bug.
- Greedy (largest coin first) fails in general (, amount : greedy , optimal ) but is correct for canonical systems like standard currency; the DP is correct for any denominations.
- Perfect Squares (squares as coins) and Word Break (dictionary words as coins over string prefixes) are the same unbounded-DP shape.
Footnotes
- Skiena, § — Knapsack / Coin Change: making change as unbounded knapsack, and pseudo-polynomial in the amount. ↩
- Erickson, Ch. — Dynamic Programming: combinations vs. compositions and how the nesting of the item and target loops selects between unordered and ordered counts. ↩
- Skiena, § — Knapsack / Coin Change: greedy change-making is optimal only for canonical denomination systems; the DP is correct for arbitrary coins. ↩
- Kozen & Zaks (1994) bound the smallest greedy-counterexample below , and Pearson (2005) gives an canonicity test; Ramírez Alfonsín (1996) on the NP-hardness of the Frobenius number. The counting DP is the coefficient extraction of , Euler's partition generating function. ↩
╌╌ END ╌╌