0 1 Knapsack Problem in DSA Python - Time & Space Complexity
We want to understand how the time needed to solve the 0 1 Knapsack problem grows as we add more items or increase the knapsack capacity.
How does the number of steps change when the input size changes?
Analyze the time complexity of the following code snippet.
def knapsack(weights, values, capacity):
n = len(weights)
dp = [[0] * (capacity + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(capacity + 1):
if weights[i-1] <= w:
dp[i][w] = max(dp[i-1][w], dp[i-1][w - weights[i-1]] + values[i-1])
else:
dp[i][w] = dp[i-1][w]
return dp[n][capacity]
This code solves the 0 1 Knapsack problem using a table to store solutions for smaller problems.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The nested loops that fill the dp table.
- How many times: The outer loop runs once for each item (n times), and the inner loop runs once for each capacity value from 0 to capacity (capacity + 1 times).
As we add more items or increase the capacity, the number of steps grows by multiplying these two numbers.
| Input Size (n items, capacity W) | Approx. Operations |
|---|---|
| 10 items, capacity 50 | About 10 * 51 = 510 |
| 100 items, capacity 1000 | About 100 * 1001 = 100,100 |
| 1000 items, capacity 5000 | About 1000 * 5001 = 5,001,000 |
Pattern observation: The steps grow roughly by multiplying the number of items and the capacity, so doubling either doubles the work.
Time Complexity: O(n * W)
This means the time needed grows proportionally with both the number of items and the knapsack capacity.
[X] Wrong: "The time complexity is just O(n) because we only loop through the items once."
[OK] Correct: The inner loop over capacities also runs many times, so the total steps depend on both items and capacity, not just items.
Understanding this time complexity helps you explain how dynamic programming improves over simple recursion and why the problem scales with both items and capacity.
"What if we used a recursive solution without memoization? How would the time complexity change?"
Practice
Solution
Step 1: Understand the problem goal
The 0 1 Knapsack Problem tries to select items to maximize total value without exceeding the weight capacity.Step 2: Clarify constraints
Each item can be chosen only once (0 or 1 times), so the goal is to maximize value under weight limit.Final Answer:
The total value of items without exceeding the weight limit -> Option AQuick Check:
Maximize value under weight limit = The total value of items without exceeding the weight limit [OK]
- Confusing weight maximization with value maximization
- Thinking the problem counts items instead of values
- Ignoring the weight limit constraint
dp of size (n+1) x (W+1) filled with zeros for the 0 1 Knapsack DP table?Solution
Step 1: Understand 2D list initialization
Using list comprehension with nested loops creates independent inner lists, avoiding shared references.Step 2: Analyze each option
dp = [[0 for _ in range(W+1)] for _ in range(n+1)] correctly uses nested list comprehension to create a (n+1) x (W+1) zero matrix.Final Answer:
dp = [[0 for _ in range(W+1)] for _ in range(n+1)] -> Option AQuick Check:
Nested list comprehension for 2D zero matrix = dp = [[0 for _ in range(W+1)] for _ in range(n+1)] [OK]
- Using multiplication for inner lists causing shared references
- Creating 1D list instead of 2D
- Swapping dimensions incorrectly
def knapsack(weights, values, W):
n = len(weights)
dp = [[0]*(W+1) for _ in range(n+1)]
for i in range(1, n+1):
for w in range(1, W+1):
if weights[i-1] <= w:
dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1])
else:
dp[i][w] = dp[i-1][w]
return dp[n][W]
print(knapsack([1,2,3], [10,20,30], 5))Solution
Step 1: Trace DP table filling
Items: (1,10), (2,20), (3,30), Capacity=5. Check combinations:
- Take items 2 and 3: weight=5, value=50
- Take items 1 and 3: weight=4, value=40
- Take all items 1,2,3: weight=6 (exceeds capacity)
- Take items 1 and 2: weight=3, value=30Step 2: Find max value within capacity
Best is items 2 and 3 with total value 50, but check if better:
Actually, items 1,2,3 total weight 6 > 5, so not allowed.
Items 1,2,3 can't all be taken.
Check items 1,2,3 subsets:
Items 2,3: 5 weight, 50 value
Items 1,3: 4 weight, 40 value
Items 1,2: 3 weight, 30 value
So max value is 50.Final Answer:
50 -> Option CQuick Check:
Max value under weight 5 = 50 [OK]
- Assuming all items fit without checking weight
- Mixing up indices in DP table
- Forgetting to subtract weight when adding value
def knapsack(weights, values, W):
n = len(weights)
dp = [[0]*(W+1) for _ in range(n+1)]
for i in range(1, n+1):
for w in range(1, W+1):
if weights[i] <= w:
dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i]] + values[i])
else:
dp[i][w] = dp[i-1][w]
return dp[n][W]Solution
Step 1: Identify index error in array access
Python lists are zero-indexed, but loop uses i from 1 to n, so weights[i] and values[i] cause IndexError or wrong access.Step 2: Correct indexing for weights and values
Use weights[i-1] and values[i-1] to access correct item corresponding to dp row i.Final Answer:
Change weights[i] to weights[i-1] and values[i] to values[i-1] -> Option DQuick Check:
Fix indexing to avoid off-by-one errors = Change weights[i] to weights[i-1] and values[i] to values[i-1] [OK]
- Using i instead of i-1 for zero-based lists
- Changing dp indices incorrectly
- Altering loop ranges unnecessarily
Solution
Step 1: List possible item combinations under capacity 5
Check combinations:
- Item 1 (2,3) + Item 2 (3,4): total weight 5, value 7
- Item 3 (4,5) alone: weight 4, value 5
- Item 4 (5,6) alone: weight 5, value 6
- Item 2 (3,4) + Item 3 (4,5): weight 7 (exceeds capacity)
- Item 1 (2,3) + Item 3 (4,5): weight 6 (exceeds capacity)Step 2: Choose combination with max value
Items 1 and 2 together fit exactly and give value 7, which is higher than any single item value under capacity.Final Answer:
Select items with weights 2 and 3; max value 7 -> Option BQuick Check:
Max value with capacity 5 = 7 by items 2 and 3 weights [OK]
- Choosing heavier single item over lighter combo
- Ignoring exact capacity limit
- Assuming max value is from single item
Ready to prove it?
Pass the test and this pattern moves unknown
