Abstract

Suppose you are a burglar who breaks into a store and want to leave with the maximum value of items. Your knapsack can only hold 13 lbs, and the items in the store are:

Value4912151921
Weight245789

What is the maximum value you can carry, given a list of items where each item has value and weight , and total weight can’t exceed capacity ?

  • Category: Dynamic Programming / Combinatorial Optimization
  • Input: Items with values and weights ; capacity
  • Output: Maximum total value achievable without exceeding capacity
  • Paradigm: Backtracking (naive) → Dynamic Programming
  • Typical use cases: resource allocation under a budget/capacity constraint; the classic example distinguishing unbounded (items reusable) from 0/1 (each item used at most once) variants

This Is the Unbounded Variant

In the backtracking pseudocode below, including item recurses on BTKS(w[1...n], v[1...n], C - w[n])the full item list, including again — rather than w[1...n-1]. That’s what makes this Unbounded Knapsack: an item can be picked more than once. The classic 0/1 Knapsack (each item used at most once) would instead recurse on w[1...n-1] in the “include” branch.


Problem Specification

  • Instance: Items , each with a value and weight ; a capacity .
  • Solution Format: A multiset of items (repeats allowed) to carry.
  • Constraints: Total weight of chosen items .
  • Objective: over chosen items (with repetition).
  • Goal: Maximize.

Candidate Strategies / Approaches

Backtracking ✘

Algorithm 21 Knapsack Problem

procedure BTKS(w[1n],v[1n],Cw[1 \dots n], v[1 \dots n], C)

if C=0C = 0 or n=0n=0 then

return 00

if w[n]>Cw[n] > C then

return BTKS(w[1n1],v[1n1],C)BTKS(w[1 \dots n-1], v[1 \dots n-1], C)

In = v(n)+BTKS(w[1n],v[1n],Cw[n])v(n) + BTKS(w[1\dots n], v[1 \dots n], C - w[n])

Out = BTKS(w[1n1],v[1n1],C)BTKS(w[1 \dots n-1], v[1 \dots n-1], C)

return max(\max(In,, Out))

Runtime

Unlike most of this vault’s backtracking examples, doesn’t strictly decrease on every call (the In branch keeps the same item list) — only strictly decreases there (by at least each time), while Out strictly decreases . So recursion depth is bounded by roughly , but the branching at every level still makes this exponential in the worst case — no better than exhaustive search, same story as Weighted Event Scheduling’s BTWES.

Dynamic Programming ✔

Replace the recursive calls with an array value: let be the maximum value you can fit in a -capacity knapsack using only items .


Dynamic Programming Solution

1. Define Subproblems

Let be the maximum value you can fit in a -capacity knapsack using only items .

2. Base Cases

(No capacity, or no items available, both trivially cap value at 0.)

3. Recursion Used to Fill the Array

  • Out: item is not included .
  • In: item is included — note this stays on row , allowing item to be reused.

Since we don’t know which is bigger, compute both and take the max:

(if , item doesn’t fit, so ).

4. Ordering of the Subproblems

Cell depends on (same row, to its left) and (row above, same column).

So the problems can be ordered by filling each row left to right, starting from the top row and working down:

for j = 1 ... n
    for b = 1 ... C

5. Final Output

6. Runtime

One cell per pair, work each.


Worked Example

Given:

ValueWeight
42
94
125
157
198
219

Completed Solution Table:

012345678910111213
00000000000000
420044881212161620202424
940044991313181822222727
12500449121316182124252830
15700449121316182124252830
19800449121316192124252831
21900449121316192124252831

Reading a cell — row 3 (), :

Patterns Worth Noticing

  • Row 4 (item ) is identical to Row 3. Its value-per-weight ratio () is worse than item 3’s (), so it never wins a comparison — adding a row to the table doesn’t guarantee the answers change.
  • Item 3 has the best value/weight ratio of all six (2.4, vs. the next-best 2.375 for item 5/8). For unbounded knapsack, as capacity grows large, the optimal strategy converges toward “just take the best-ratio item repeatedly” — part of why item 3’s influence dominates the later columns.

Variables & Data Structures

NameTypePurpose
KS2D array, KS[j][b] = max value using items with capacity
jRow indexWhich prefix of items is currently allowed
bColumn indexRemaining capacity being considered

Helper Functions / Operations Used

  • Table lookup per cell, reading KS[j-1][b] and KS[j][b-w(j)].

Proof of Correctness / Optimality

Claim: equals the true maximum value achievable using only items within capacity .

  • Base cases: (no capacity, nothing fits) and (no items available) are both correct by inspection — the empty selection is the only option, with value 0.
  • Inductive Hypothesis: every cell computed before in the row-by-row, left-to-right order — i.e. and for — is correct.
  • Inductive Step: any valid selection using items within capacity either uses item at least once or doesn’t:
    • Doesn’t use item : the best such selection is exactly the best selection using only items , i.e. — correct by the Inductive Hypothesis.
    • Uses item (at least once): taking one copy of item leaves capacity , still allowing item to be reused, so the best such selection is — correct by the Inductive Hypothesis, since .
  • Since of these two cases, and every valid selection falls into exactly one of them, is the true maximum.

Time & Space Complexity Analysis

General Case

ComplexityNotes
TimeOne computation per cell, cells total
SpaceThe full table, though this can be reduced to by only keeping the current and previous row if the item selection itself doesn’t need to be reconstructed

Best / Worst / Average Case

  • Best / Worst / Average case: all — every cell is filled regardless of the specific values/weights involved.

Pseudo-Polynomial Runtime

looks polynomial, but it’s polynomial in the value of , not in the size of its binary representation. Since only takes bits to write down, this runtime is exponential in the actual input size when is large — this is the standard example of a pseudo-polynomial algorithm, and it’s exactly why Knapsack is still NP-hard in general despite this DP solution existing.


Drawbacks / Constraints

  • Pseudo-polynomial time — see the callout above; this DP approach becomes impractical when is astronomically large relative to , even though the table-filling logic itself is simple.
  • Unbounded vs. 0/1 matters. This solution assumes items are reusable. For the 0/1 variant (each item usable at most once), the “In” recursion must reference instead of — forgetting this distinction silently solves the wrong problem.
  • Space can be reduced if only the optimal value is needed (not which items were chosen) — see the Space row above.

References / Links