0/1 Knapsack
A DP table that decides to take or leave each item to fit the highest total value into a knapsack of limited capacity.
Worth knowing first
#How does it work?
In the 0/1 knapsack problem every item has a weight and a value, and the knapsack can only carry so much weight. Each item is either taken or left (0/1), and the goal is the highest total value.
A greedy approach (e.g. taking the best value-per-weight first) can get it wrong. Trying every subset means 2ⁿ options.
In the DP table, dp[i][c] is the best value using the first i items with capacity c. Each cell has two options: leave item i (dp[i − 1][c]) or, if it fits, take it (dp[i − 1][c − w] + v). The larger one wins.
Once the table is full, walk back from the bottom right: if a cell differs from the one above it, that item was taken.
#Try it yourself
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1: w1 v1 | ||||||||
| 2: w3 v4 | ||||||||
| 3: w4 v5 | ||||||||
| 4: w5 v7 |
Rows are items (w: weight, v: value), columns are the knapsack's capacity. Each cell holds the best value using the items so far within that capacity.
#Step by step
- 01dp[0][c] = 0: with no items the value is 0.
- 02For each item i (weight w, value v) and each capacity c: leaving it gives dp[i − 1][c].
- 03If w ≤ c, taking it gives dp[i − 1][c − w] + v; write the larger of the two to dp[i][c].
- 04dp[n][W] is the answer; walk back from the bottom right to find the items.
#Example code
The same algorithm in 8 languages. Every program is compiled, run and checked to print the same output.
# (weight, value) of each item.
ITEMS = [(1, 1), (3, 4), (4, 5), (5, 7)]
W = 7
n = len(ITEMS)
# dp[i][c] = best value using the first i items with capacity c.
dp = [[0] * (W + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
w, v = ITEMS[i - 1]
for c in range(W + 1):
dp[i][c] = dp[i - 1][c] # skip item i
if w <= c:
dp[i][c] = max(dp[i][c], dp[i - 1][c - w] + v) # or take it
# Walk back: a value that differs from the row above means item i was taken.
taken, c = [], W
for i in range(n, 0, -1):
if dp[i][c] != dp[i - 1][c]:
taken.append(i)
c -= ITEMS[i - 1][0]
print(dp[n][W])
print(" ".join(map(str, reversed(taken)))) # 1-based item numbers#Complexity
| Operation | Time |
|---|---|
| Time | O(n · W) |
| Memory (full table) | O(n · W) |
| Memory (one row) | O(W) |
| Trying every subset | O(2ⁿ) |
n is the number of items and W the capacity. O(n · W) looks polynomial but depends on the numeric value of W (pseudo-polynomial): a huge capacity means a huge table. With a single row, iterating capacities from high to low, memory drops to O(W).
#When to use it?
"Pick the most value within a limited capacity" problems: budgeting and resource allocation, portfolio selection, cargo loading, choosing topics to study before an exam. The same table idea powers subset sum and coin-change problems.
#Practice
Log in to answer questions and save your progress.
Easy
What does "0/1" mean in the 0/1 knapsack problem?
- 1Each item is either taken whole or not at all
- 2Values are 0 or 1
- 3The capacity is 0 or 1
- 4Items can be split
What does dp[i][c] hold?
- 1The best value using the first i items with capacity c
- 2Item i's weight
- 3The number of items of weight c
- 4The remaining capacity
What is this DP solution's time complexity (n items, capacity W)?
- 1O(n · W)
- 2O(2ⁿ)
- 3O(n log n)
- 4O(W²)
Medium
Capacity 7; items (weight, value): (3, 4), (4, 5), (5, 7). What is the best value?
- 19
- 27
- 312
- 411
Hard
When reducing memory to O(W) with one row, why are capacities visited from high to low?
- 1So the same item isn't taken twice in one round
- 2Because it is faster
- 3To avoid negative values
- 4To keep the array sorted
#Up next
Lessons that build on this one: