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:
Value 4 9 12 15 19 21 Weight 2 4 5 7 8 9 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 thanw[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 onw[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()
if or then
return
if then
return
In =
Out =
return InOut
Runtime
Unlike most of this vault’s backtracking examples, doesn’t strictly decrease on every call (the
Inbranch keeps the same item list) — only strictly decreases there (by at least each time), whileOutstrictly 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’sBTWES.
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:
| Value | Weight |
|---|---|
| 4 | 2 |
| 9 | 4 |
| 12 | 5 |
| 15 | 7 |
| 19 | 8 |
| 21 | 9 |
Completed Solution Table:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||
| 4 | 2 | 0 | 0 | 4 | 4 | 8 | 8 | 12 | 12 | 16 | 16 | 20 | 20 | 24 | 24 |
| 9 | 4 | 0 | 0 | 4 | 4 | 9 | 9 | 13 | 13 | 18 | 18 | 22 | 22 | 27 | 27 |
| 12 | 5 | 0 | 0 | 4 | 4 | 9 | 12 | 13 | 16 | 18 | 21 | 24 | 25 | 28 | 30 |
| 15 | 7 | 0 | 0 | 4 | 4 | 9 | 12 | 13 | 16 | 18 | 21 | 24 | 25 | 28 | 30 |
| 19 | 8 | 0 | 0 | 4 | 4 | 9 | 12 | 13 | 16 | 19 | 21 | 24 | 25 | 28 | 31 |
| 21 | 9 | 0 | 0 | 4 | 4 | 9 | 12 | 13 | 16 | 19 | 21 | 24 | 25 | 28 | 31 |
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
| Name | Type | Purpose |
|---|---|---|
KS | 2D array, | KS[j][b] = max value using items with capacity |
j | Row index | Which prefix of items is currently allowed |
b | Column index | Remaining capacity being considered |
Helper Functions / Operations Used
- Table lookup — per cell, reading
KS[j-1][b]andKS[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
| Complexity | Notes | |
|---|---|---|
| Time | One computation per cell, cells total | |
| Space | The 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.