Unbounded Knapsack Problem in DSA Go - Time & Space Complexity
We want to understand how the time needed to solve the Unbounded Knapsack Problem grows as the input size changes.
Specifically, how does the number of items and the knapsack capacity affect the work done?
Analyze the time complexity of the following code snippet.
func unboundedKnapsack(W int, wt []int, val []int, n int) int {
dp := make([]int, W+1)
for i := 0; i <= W; i++ {
for j := 0; j < n; j++ {
if wt[j] <= i {
dp[i] = max(dp[i], dp[i-wt[j]] + val[j])
}
}
}
return dp[W]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
This code finds the maximum value achievable with unlimited copies of items within capacity W.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops: outer loop runs from 0 to W, inner loop runs over all n items.
- How many times: Outer loop runs W+1 times, inner loop runs n times each outer iteration.
As the knapsack capacity W grows, the outer loop runs more times. For each capacity, we check all n items.
| Input Size (W) | Approx. Operations (W * n) |
|---|---|
| 10 | 10 * n |
| 100 | 100 * n |
| 1000 | 1000 * n |
Pattern observation: The work grows linearly with both capacity W and number of items n.
Time Complexity: O(W * n)
This means the time needed grows proportionally with the knapsack capacity and the number of items.
[X] Wrong: "The time complexity depends only on the number of items n."
[OK] Correct: The capacity W also affects the number of steps because the algorithm checks every capacity from 0 to W.
Understanding this time complexity helps you explain how dynamic programming solves problems efficiently by breaking them into smaller parts.
"What if we changed the problem to allow only one copy of each item? How would the time complexity change?"
Practice
Unbounded Knapsack Problem and the 0/1 Knapsack Problem?Solution
Step 1: Understand item selection rules
In 0/1 Knapsack, each item can be selected at most once. In Unbounded Knapsack, items can be selected multiple times without limit.Step 2: Compare problem constraints
The key difference is the repetition allowance of items, which changes the approach to solving the problem.Final Answer:
In Unbounded Knapsack, items can be chosen unlimited times; in 0/1 Knapsack, each item can be chosen only once. -> Option CQuick Check:
Item repetition rule differs = A [OK]
- Confusing item repetition rules
- Thinking 0/1 Knapsack allows multiple picks
- Mixing fractional knapsack with unbounded knapsack
W?Solution
Step 1: Understand DP array size requirement
The DP array stores maximum values for capacities from 0 to W inclusive, so it needs length W+1.Step 2: Check Go slice initialization
Using make([]int, W+1) creates a slice of length W+1, which covers all capacities including zero.Final Answer:
dp := make([]int, W+1) -> Option AQuick Check:
DP array length = W+1 [OK]
- Using dp size W instead of W+1
- Using negative or zero size slices
- Confusing capacity with number of items
package main
import "fmt"
func unboundedKnapsack(W int, wt []int, val []int) int {
dp := make([]int, W+1)
for i := 0; i <= W; i++ {
for j := 0; j < len(wt); j++ {
if wt[j] <= i {
if dp[i] < dp[i-wt[j]] + val[j] {
dp[i] = dp[i-wt[j]] + val[j]
}
}
}
}
return dp[W]
}
func main() {
W := 5
wt := []int{1, 3, 4}
val := []int{10, 40, 50}
fmt.Println(unboundedKnapsack(W, wt, val))
}Solution
Step 1: Trace DP array updates for capacity 5
Calculate max value for each capacity: - i=1: max(dp[1], dp[0]+10)=10 - i=2: max(dp[2], dp[1]+10)=20 - i=3: max(dp[3], dp[0]+40, dp[2]+10)=40 - i=4: max(dp[4], dp[1]+40, dp[0]+50)=50 - i=5: max(dp[5], dp[2]+40, dp[1]+50)=90Step 2: Final DP value at capacity 5
dp[5] = 90, which is the maximum value achievable with unlimited items.Final Answer:
90 -> Option AQuick Check:
Max value at W=5 is 90 [OK]
- Ignoring multiple item picks
- Using 0/1 knapsack logic
- Incorrect dp array indexing
func unboundedKnapsack(W int, wt []int, val []int) int {
dp := make([]int, W)
for i := 1; i <= W; i++ {
for j := 0; j < len(wt); j++ {
if wt[j] <= i {
dp[i] = max(dp[i], dp[i-wt[j]] + val[j])
}
}
}
return dp[W]
}Solution
Step 1: Check dp slice size and indexing
dp is created with size W, but code accesses dp[W], which is out of range. Also, dp should cover 0 to W inclusive, so size must be W+1.Step 2: Adjust loop range and dp size
Loop i should start from 0 or 1 and go up to W inclusive. dp size must be W+1 to avoid index errors.Final Answer:
Change dp size to W+1 and loop i from 0 to W inclusive -> Option DQuick Check:
dp size = W+1 and loop i ≤ W [OK]
- Using dp size W but accessing dp[W]
- Looping beyond dp slice length
- Not initializing dp correctly
wt = [2, 3, 5] and values val = [50, 70, 100], and a knapsack capacity W = 8, what is the maximum value achievable using the Unbounded Knapsack approach?Solution
Step 1: Calculate max values for capacities up to 8
Calculate dp array stepwise: - i=0: 0 - i=1: 0 (no item fits) - i=2: max(0, dp[0]+50)=50 - i=3: max(0, dp[0]+70)=70 - i=4: max(dp[4], dp[2]+50)=100 - i=5: max(dp[5], dp[0]+100, dp[3]+50)=120 - i=6: max(dp[6], dp[4]+50, dp[3]+70)=170 - i=7: max(dp[7], dp[5]+50, dp[4]+70)=190 - i=8: max(dp[8], dp[6]+50, dp[5]+70, dp[3]+100)=270Step 2: Identify maximum value at capacity 8
dp[8] = 270 is the highest value achievable with unlimited items.Final Answer:
270 -> Option BQuick Check:
Max value at W=8 is 270 [OK]
- Not considering multiple picks of same item
- Calculating dp values incorrectly
- Ignoring smaller capacity combinations
Ready to prove it?
Pass the test and this pattern moves unknown
