0/1 Knapsack
Given items with nonnegative integer weights and values , choose each item at most once to maximize total value under capacity :
State and transition
Let dp[c] be the best value achievable with processed items and capacity c.
For each item, update capacities in descending order:
def knapsack(weights: list[int], values: list[int], capacity: int) -> int:
dp = [0] * (capacity + 1)
for weight, value in zip(weights, values, strict=True):
for current in range(capacity, weight - 1, -1):
dp[current] = max(dp[current], dp[current - weight] + value)
return dp[capacity]
Descending order ensures dp[current - weight] still belongs to the previous
item layer, so an item cannot be reused. Ascending order instead implements an
unbounded-use transition.
Cost and interpretation
- Time: .
- Value-only space: .
- Reconstructing selected items needs stored decisions or recomputation.
This runtime is pseudo-polynomial: it is polynomial in numeric capacity , not in the number of bits needed to encode .
Variants are different problems
- Fractional knapsack permits splitting items and has a greedy solution.
- Unbounded knapsack permits unlimited reuse and changes update order.
- Bounded knapsack supplies a finite multiplicity per item.
State the variant before choosing a recurrence.