Given items each with a weight and value, and a knapsack with a weight capacity, maximize value. The variants (0/1, unbounded, bounded multiplicity) teach the full DP toolkit.
0/1 knapsack
Each item is either taken (once) or not. State: = best value using items with capacity . Recurrence:
Take or skip. time and space, where is the capacity. (This is pseudo-polynomial — polynomial in but exponential in the bit-length of . NP-hard in general.)
Space optimization
The recurrence only references , so collapse to a 1D array. Important: iterate the inner loop from down to , not up. Going up would let the same item be picked multiple times — that's a different problem.
Unbounded knapsack
Unlimited copies of each item. Same DP, but iterate the inner loop UP from to . This way, updated with item at weight can be reused for , allowing multiple copies.
Coin change (count of ways)
Count the number of ways to make change for using unlimited copies of each coin. Identical to unbounded knapsack with "value" replaced by "count of ways."
Why these matter
The shape of the recurrence — "for each item, either take or don't" — appears constantly. Subset sum, partition equal subset sum, target sum, last stone weight — all variants of the same template. Master 0/1 knapsack and a third of DP problems become routine.
NP-hardness
General knapsack with arbitrary-precision weights is NP-hard. The pseudo-polynomial algorithm works because real-world capacities are bounded. Beware: if weights can be enormous (say, ), the DP table doesn't fit in memory — you'd need approximation algorithms.