Skip to content
Work Practice Products Coding interviews Machine learning coding Quant developer HFT course Blog Method Why me About Enquire Book a call

Coding · Dynamic programming

Counting with a knapsack

Lesson · 9 min read · Dynamic programming · Counting knapsack

Replace max with sum and the knapsack counts the ways instead of finding the best. The loop order decides whether you count combinations or sequences.

Max becomes sum

"In how many ways can amount a be paid with these coins?" is the unbounded knapsack with the operators swapped: instead of the best value over the choices, add up the number of ways over them. The base case is one way to pay 0: pay nothing.

Counting recurrence

ways(i, a)

  1. ways(i − 1, a) (no more of coin i) + ways(i, a − coin[i]) (one more of coin i).

base cases

  1. ways(·, 0) = 1; ways(0, a) = 0 for a > 0; negative amounts give 0.

Combinations, not sequences

As the recurrence lesson showed, looping over amounts on the outside and coins on the inside counts 1 + 2 and 2 + 1 separately: sequences. Putting coins on the outside processes each coin type once, in a fixed order, so each combination is built in exactly one way.

def count_ways(coins, amount):
    ways = [1] + [0] * amount
    for c in coins:                            # coins outside: combinations
        for a in range(c, amount + 1):
            ways[a] += ways[a - c]
    return ways[amount]

Coins 1, 2, 5; amount 5. One pass per coin type.

  1. Step 1 / 4
    ways
    100102030405

    One way to pay 0; no way yet to pay anything else.

  2. Step 2 / 4
    ways
    101112131415

    After allowing coin 1: ways[a] += ways[a − 1] for a from 1 up to 5.

  3. Step 3 / 4
    ways
    101122233435

    After allowing coin 2: ways[a] += ways[a − 2] for a from 2 up to 5.

  4. Step 4 / 4
    ways
    101122233445

    After allowing coin 5: ways[a] += ways[a − 5] for a from 5 up to 5. ways[5] = 4.

Implementation

In six languages:

def count_ways(amount, coins):
    ways = [1] + [0] * amount                # one way to make 0
    for c in coins:                          # coins outside: combinations, not sequences
        for a in range(c, amount + 1):
            ways[a] += ways[a - c]
    return ways[amount]

The same code is the reference solution to Ways to make an amount, where it is compiled and run against the tests in all six languages.

TimeO(coins · amount)
SpaceO(amount)

Swap the loops (amounts outside, coins inside) and the same code counts ordered sequences: the number of ways to climb a staircase with steps of the given sizes.