Dynamic Programming · Classic DP Patterns

Unbounded Knapsack Patterns

Filling a 1D table where each item can be reused any number of times, by iterating capacity in increasing order and referencing the CURRENT row's own smaller-capacity answer (not a previous row's) - the one-line change from 0/1 knapsack's iteration order that allows unlimited reuse instead of at-most-once.

  • 5 min read
  • 8 practice questions
  • 3 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: counting orderings
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Some knapsack problems let you reuse items: coins you can spend as many times as you like, or rod pieces you can cut in any number. The item dimension of 0/1 knapsack existed only to stop reuse, so it can go. One table indexed by amount, or by length, is enough. What stays subtle is what gets counted: combinations, where 1 + 2 and 2 + 1 are the same, or orderings, where they're different. The loop order decides which one you get.

We'll go from simple to more technical:

  1. The pattern: one table by amount, swept upward so items can repeat, with coins in the outer loop for combinations.
  2. A second flavor: amount in the outer loop to count orderings.
  3. Where unbounded knapsack shows up (making change, cutting stock).
  4. What breaks it: loop order, sweep direction, greedy ratios, big counts.

The approach

Java

// Coin Change II: number of combinations (multisets) of coins summing to amount
int change(int amount, int[] coins) {
    int[] ways = new int[amount + 1];
    ways[0] = 1;                                   // one way to make 0: use nothing
    for (int coin : coins) {                       // coins OUTER: each multiset counted once
        for (int s = coin; s <= amount; s++) {     // upward: ways[s - coin] may already use this coin - reuse is allowed
            ways[s] += ways[s - coin];
        }
    }
    return ways[amount];
}

// Rod Cutting: best total price, any number of pieces of each length
int rodCutting(int n, int[] price) {
    int[] best = new int[n + 1];
    for (int len = 1; len <= n; len++)
        for (int piece = 1; piece <= len; piece++)
            best[len] = Math.max(best[len], price[piece - 1] + best[len - piece]);   // best[len - piece] may reuse piece
    return best[n];
}

Python

# Coin Change II: number of combinations (multisets) of coins summing to amount
def change(amount: int, coins: list[int]) -> int:
    ways = [0] * (amount + 1)
    ways[0] = 1                                    # one way to make 0: use nothing
    for coin in coins:                             # coins OUTER: each multiset counted once
        for s in range(coin, amount + 1):          # upward: ways[s - coin] may already use this coin - reuse is allowed
            ways[s] += ways[s - coin]
    return ways[amount]


# Rod Cutting: best total price, any number of pieces of each length
def rod_cutting(n: int, price: list[int]) -> int:
    best = [0] * (n + 1)
    for length in range(1, n + 1):
        for piece in range(1, length + 1):
            best[length] = max(best[length], price[piece - 1] + best[length - piece])   # best[length - piece] may reuse piece
    return best[n]

Sweeping amounts upward is exactly what 0/1 knapsack forbids and unbounded knapsack wants: ways[s - coin] may already include this coin, so the coin can be used again. Coins in the outer loop fix the order in which coins are considered, so every combination is built in one canonical order (all 1s, then all 2s, ...) and counted once. Coin Change I (fewest coins) is the same table with min in place of +.

A second flavor: counting orderings

Swap the loops - amount outside, coins inside - and at each amount every coin may be the last one added. Now 1+2 and 2+1 are different sequences:

Java

int[] ways = new int[target + 1];
ways[0] = 1;
for (int s = 1; s <= target; s++)                  // amount OUTER
    for (int x : nums)
        if (x <= s) ways[s] += ways[s - x];        // ending with x

Python

ways = [0] * (target + 1)
ways[0] = 1
for s in range(1, target + 1):                     # amount OUTER
    for x in nums:
        if x <= s:
            ways[s] += ways[s - x]                 # ending with x

Coins [1, 2] and amount 3: coins-outer counts {1,1,1} and {1,2} - 2; amount-outer counts 1+1+1, 1+2 and 2+1 - 3. Neither is wrong; they answer different questions ("how many ways to pay" versus "how many ways to climb with steps of these sizes"), and the loop order decides which one you get.

Where you'll see it

Cutting stock (how to cut standard-length rolls, boards or pipes into ordered pieces with the least waste), making change and dispensing ATM notes, and packing repeated standard units (servers, containers, ad slots of a few fixed lengths) are unbounded knapsack. Counting orderings shows up in combinatorics of schedules and step sequences.

Which tool for which job?

You needUse
Ways to pay (combinations)coins in the outer loop, amounts upward
Ways to reach a total in order (compositions)amount in the outer loop, coins inside
Fewest coinsthe same table with min
Fewest coins, canonical systems (US, euro)greedy, once canonicity is checked
Payable or not, for huge amountsshortest paths on residues mod the smallest coin
Each item limited to k copiesbinary splitting into 0/1 items

Common mistakes

MistakeWhat happensDo this instead
The wrong loop ordercombinations and orderings mixed up (2 vs 3 for [1, 2], 3)name which one the problem wants
Sweeping downwardeach coin used at most onceupward for unbounded
Greedy by price per length31 instead of 32 for the rod exampleDP
Greedy fewest coins on any system{1, 3, 4} for 6 gives 3 coins instead of 2DP, or check the system is canonical

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Coins [1, 2], amount 3. What do coins-outer and amount-outer loops count?

Show answer

Coins-outer: 2 combinations ({1, 1, 1} and {1, 2}). Amount-outer: 3 orderings (1+1+1, 1+2, 2+1). With coins outer, each multiset is built in one fixed coin order; with amount outer, every coin can be the last one added at every amount.

2. Why does the unbounded version sweep amounts upward while 0/1 knapsack sweeps downward?

Show answer

Sweeping upward, ways[s - coin] has already been updated with this coin, so building on it uses the coin again - reuse is the point. 0/1 knapsack sweeps downward precisely so that smaller entries still reflect the state before the current item.

3. Why is ways[0] = 1?

Show answer

There is exactly one way to make 0: take nothing. Every count is built by extending smaller amounts, so this single "empty" way is what the first coin of every combination extends. With ways[0] = 0, every count stays 0.

Key takeaways

  • Unbounded knapsack uses one table swept upward, so items can be reused.
  • Coins outer counts combinations; amount outer counts orderings.
  • ways[0] = 1 (the empty choice) seeds every count.
  • Fewest coins and rod cutting are the same table with min or max instead of +.
  • Greedy by ratio fails; the DP is O(items * amount), pseudo-polynomial.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Combinationscoins outer, upward
Orderingsamount outer
Fewest coinsmin over the same table
Huge amounts, payable?residues mod the smallest coin
Limited copiesbinary splitting

Snippets worth knowing by heart

Java

int[] ways = new int[amount + 1];
ways[0] = 1;
for (int c : coins)                                   // coins OUTER: combinations
    for (int v = c; v <= amount; v++) ways[v] += ways[v - c];

Python

p = 1                                    # binary splitting of k copies
while k > 0:
    take = min(p, k)
    parts.append((w * take, v * take))
    k -= take
    p *= 2

The traps

  • Loop order: coins outside counts combinations, amounts outside counts orderings. Decide which the problem wants.
  • Downward sweep: each item is used at most once. Sweep upward for unbounded reuse.
  • Greedy ratios: the best price per length isn't the best total. Use DP.
  • Greedy coins on a non-canonical set: {1, 3, 4} pays 6 with three coins instead of two. Use DP.

Staff-level points, one line each

  • Greedy never lost for US or euro coins. It lost at 6 for {1, 3, 4}, at 30 for {1, 10, 25} and at 14 for {1, 7, 10}.
  • Checking below the sum of the two largest coins found the same first failure as checking to 300, on 200 random systems.
  • {6, 9, 20}: the largest unpayable amount is 43, via residues mod 6. 10^15 + 7 is payable.
  • Binary splitting: 102 items instead of 765, with the same best value, 3,117.

Say it in an interview

  • "Unbounded means sweeping upward, so an item can be reused in the same pass."
  • "Coins outside counts combinations, the amount outside counts orderings."
  • "Greedy is only safe on canonical systems, and I can check that with a bounded DP."
  • "For astronomically large amounts I'd use shortest paths on residues."

Practice

This skill comes with 8 multiple-choice questions that tell you which misconception a wrong answer reveals and 3 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Unbounded Knapsack Patterns free

Coin Change II

MediumProCoding problemJava · Python

Count the different ways to make amount from coins whose values are listed in coins, with an unlimited supply of each. Order doesn't matter: 1 + 2 and 2 + 1 are the same way.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Rod Cutting

MediumProCoding problemJava · Python

Given a rod of length n and a price array where price[i] is the price of a piece of length i + 1, find the maximum total value obtainable by cutting the rod into pieces of any lengths (using each length any number of times) and selling them.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Combination Sum IV (Ordered Counts)

MediumProCoding problemJava · Python

Using numbers from nums (all different, each usable as often as you like), count the ordered sequences that add up to target. The same numbers in a different order count as a different sequence.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Unbounded Knapsack Patterns free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime