Unbounded Knapsack Problem in DSA Python - Time & Space Complexity
We want to understand how the time needed to solve the Unbounded Knapsack problem changes as the input size grows.
Specifically, how the number of items and the knapsack capacity affect the work done.
Analyze the time complexity of the following code snippet.
def unbounded_knapsack(capacity, weights, values):
dp = [0] * (capacity + 1)
for c in range(capacity + 1):
for i in range(len(weights)):
if weights[i] <= c:
dp[c] = max(dp[c], dp[c - weights[i]] + values[i])
return dp[capacity]
This code finds the maximum value achievable with unlimited copies of given items within the knapsack capacity.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops where for each capacity from 0 to max, we check all items.
- How many times: Outer loop runs (capacity + 1) times, inner loop runs for number of items each time.
As capacity or number of items grows, the total operations increase by multiplying these two factors.
| Input Size (capacity n, items m) | Approx. Operations |
|---|---|
| n=10, m=5 | ~ 10 * 5 = 50 |
| n=100, m=5 | ~ 100 * 5 = 500 |
| n=1000, m=10 | ~ 1000 * 10 = 10,000 |
Pattern observation: Operations grow linearly with both capacity and number of items.
Time Complexity: O(n * m)
This means the time needed grows proportionally to the product of knapsack capacity and number of items.
[X] Wrong: "The time complexity is just O(n) because we only loop over capacity once."
[OK] Correct: Because inside the capacity loop, we also loop over all items, so both loops multiply the work.
Understanding this time complexity helps you explain how dynamic programming efficiently solves problems with repeated choices.
"What if we sorted items by weight and stopped inner loop early? How would the time complexity change?"
Practice
0/1 Knapsack problem and the Unbounded Knapsack problem?Solution
Step 1: Understand 0/1 Knapsack constraints
In 0/1 Knapsack, each item can be chosen at most once.Step 2: Understand Unbounded Knapsack constraints
In Unbounded Knapsack, you can pick any item multiple times without limit.Final Answer:
In Unbounded Knapsack, you can use unlimited copies of each item. -> Option BQuick Check:
Unbounded means unlimited items [OK]
- Confusing Unbounded with fractional knapsack
- Thinking items can be chosen only once
- Assuming fixed number of items
dp of size W+1 with all zeros for the Unbounded Knapsack problem?Solution
Step 1: Understand list initialization in Python
To create a list of zeros of length W+1, use list multiplication: [0] * (W+1).Step 2: Check each option
dp = [0] * (W+1) correctly uses [0] * (W+1). dp = (0) * (W+1) is invalid syntax. dp = [0 for i in range(W)] creates a list of length W, not W+1. dp = list(0, W+1) is invalid syntax.Final Answer:
dp = [0] * (W+1) -> Option CQuick Check:
List * integer repeats list [OK]
- Using parentheses instead of brackets
- Off-by-one size errors
- Using invalid list constructor syntax
dp = [0] * (W+1)
for i in range(1, W+1):
for j in range(len(weights)):
if weights[j] <= i:
dp[i] = max(dp[i], dp[i - weights[j]] + values[j])
print(dp[W])Solution
Step 1: Trace dp array updates for each capacity i
For i=1: dp[1] = max(0, dp[0]+10) = 10
i=2: dp[2] = max(0, dp[1]+10) = 20
i=3: dp[3] = max(0, dp[2]+10, dp[0]+40) = max(0,30,40) = 40
i=4: dp[4] = max(0, dp[3]+10, dp[1]+40) = max(0,50,50) = 50
i=5: dp[5] = max(0, dp[4]+10, dp[2]+40) = max(0,60,60) = 60Step 2: Final dp[W] value
The code prints dp[5] which is 60.Final Answer:
60 -> Option AQuick Check:
Unbounded knapsack max value for W=5 is 60 [OK]
- Ignoring multiple item usage
- Off-by-one errors in dp indexing
- Mixing up weights and values
def unbounded_knapsack(W, weights, values):
dp = [0] * W
for i in range(1, W+1):
for j in range(len(weights)):
if weights[j] <= i:
dp[i] = max(dp[i], dp[i - weights[j]] + values[j])
return dp[W]Solution
Step 1: Check dp list initialization
dp is initialized with size W, but indices go up to W (inclusive), so dp should be size W+1.Step 2: Impact of incorrect dp size
Accessing dp[W] will cause an IndexError because dp has indices 0 to W-1 only.Final Answer:
dp list size should be W+1, not W. -> Option DQuick Check:
dp size must cover all indices up to W [OK]
- Off-by-one dp size errors
- Ignoring index out of range errors
- Unnecessary sorting of weights/values
Solution
Step 1: Calculate dp values for capacities 1 to 8
Initialize dp = [0]*9
i=1: no items fit, dp[1]=0
i=2: dp[2]=max(0, dp[0]+50)=50
i=3: dp[3]=max(0, dp[1]+50=0+50=50, dp[0]+70=70)=70
i=4: dp[4]=max(dp[4], dp[2]+50=50+50=100, dp[1]+70=0+70=70)=100
i=5: dp[5]=max(dp[5], dp[3]+50=70+50=120, dp[2]+70=50+70=120, dp[0]+100=100)=120
i=6: dp[6]=max(dp[6], dp[4]+50=100+50=150, dp[3]+70=70+70=140, dp[1]+100=0+100=100)=150
i=7: dp[7]=max(dp[7], dp[5]+50=120+50=170, dp[4]+70=100+70=170, dp[2]+100=50+100=150)=170
i=8: dp[8]=max(dp[8], dp[6]+50=150+50=200, dp[5]+70=120+70=190, dp[3]+100=70+100=170)=200Step 2: Check for better combinations
Try combinations:
Two 3-weight items + one 2-weight: 70+70+50=190
One 5-weight + one 3-weight: 100+70=170
Four 2-weight items: 50*4=200
So max value is 200 at capacity 8.Final Answer:
200 -> Option AQuick Check:
Unbounded knapsack max value for W=8 is 200 [OK]
- Ignoring multiple usage of items
- Not checking all item weights for each capacity
- Off-by-one errors in dp indexing
Ready to prove it?
Pass the test and this pattern moves unknown
