Problem Statement

0/1 Knapsack

Imagine you have a backpack that can hold only so much weight. In front of you is a pile of items, and each item has a weight and a value (how much money it is worth). You want to fill your backpack so the total value is as high as possible, without going over the weight limit. The catch: for each item you either take the whole thing or leave it. You cannot take half. That is what "0/1" means, 0 for leave it and 1 for take it, and each item can be used at most once. To solve this we use dynamic programming, which just means we build up the answer by solving small versions of the problem first and writing the answers in a table so we never redo work. We make a grid where one spot, dp[i][w], holds the best value we can get using the first i items when the backpack can hold weight w. For every item we ask one question: is it better to take this item or skip it?

mediumDynamic ProgrammingDynamic ProgrammingTime: O(n * W) · Space: O(n * W)

Signals to notice

maximize value within weight capacityeach item used at most onceinclude or exclude

Brute force first

Try all 2^n subsets, check weight and maximize value. Exponential in the number of items. It is a fair place to begin because it matches the surface of the question, yet it does not capture the deeper structure that makes the problem simpler.

The key insight

2D DP: dp[i][w] = max value using items 1.i with capacity w. For each item: either skip it (dp[i-1][w]) or include it (dp[i-1][w-weight[i]] + value[i]). Once you hold onto the right piece of information from moment to moment, the problem feels less like trial and error and more like following a shape that was there all along.

Trace it on weights=[1,3,4,5], values=[1,4,5,7], W=7

init: dp is (n+1)x(W+1) zeros; row 0 (no items) all 0
item1 (w=1,v=1): for cap>=1 dp[1][w]=1 -> dp[1]=[0,1,1,1,1,1,1,1]
item2 (w=3,v=4): cap<3 copy; cap>=3 max(prev, dp1[w-3]+4) -> dp[2]=[0,1,1,4,5,5,5,5]
item3 (w=4,v=5): cap>=4 max(prev, dp2[w-4]+5); dp[3][7]=max(5, dp2[3]+5)=max(5,9)=9 -> dp[3]=[0,1,1,4,5,6,6,9]
item4 (w=5,v=7): dp[4][7]=max(dp3[7]=9, dp3[2]+7=1+7=8)=9 -> dp[4]=[0,1,1,4,5,7,8,9]
return dp[4][7] = 9 (take items weight 3 and 4: 4+5)

What must stay true

dp[i][w] considers only items up to index i and exactly capacity w. Each item is either included (reducing remaining capacity) or excluded (keeping capacity). The better choice is taken. When you keep that truth intact, each local choice supports the larger solution instead of fighting it.

Shape of the loop

dp = grid (n+1) x (W+1) filled with 0
for i in 1..n:
  for w in 0..W:
    if weight[i-1] > w: dp[i][w] = dp[i-1][w]          # can't fit -> exclude
    else: dp[i][w] = max(dp[i-1][w],                    # exclude
                         dp[i-1][w-weight[i-1]] + value[i-1])  # include
return dp[n][W]

Pseudocode only — the full worked solution lives in the Solution tab.

Easy way to go wrong

Using the item more than once — 0/1 means each item is available exactly once. Process items in order and reference dp[i-1][..] (previous row) to prevent reuse. When the code becomes mechanical before the idea is clear, small edge cases start breaking the whole story.

Dynamic Programming Pattern