Pattern #70
Knapsack
Master resource allocation under capacity limits. Compare Take vs Skip choices across 0/1 single-use, Unbounded reuse, Subset Sum reachability, and Coin Change loop orders.
Must Solve
20 core questions — solve these first.
- 1.0/1 Knapsack1D DP right-to-left loop: dp[w] = max(dp[w], val + dp[w-wt])medium
- 2.Partition Equal Subset SumSubset sum target = sum/2; 0/1 right-to-left boolean DPmedium
- 3.Target SumTransform P = (sum + target)/2; count 0/1 subsets with sum Pmedium
- 4.Subset SumBase 0/1 boolean DP: dp[s] = dp[s] || dp[s - num]medium
- 5.Last Stone Weight IIMin subset sum difference: find max subset sum S1 <= total/2medium
- 6.Ones and Zeroes2D Capacity 0/1 DP: dp[z][o] updated backwards for each stringmedium
- 7.Profitable SchemesMulti-dimensional 0/1 DP: members limit + min profit targethard
- 8.Coin ChangeUnbounded 1D min coins DP: dp[a] = min(dp[a], 1 + dp[a-coin])medium
- 9.Coin Change IIUnbounded combination count: outer coin loop, inner amount left-to-rightmedium
- 10.Combination Sum IVUnbounded permutation count: outer amount loop, inner coin loopmedium
- 11.Perfect SquaresUnbounded min items DP: dp[i] = min(dp[i], 1 + dp[i - s*s])medium
- 12.Unbounded Knapsack1D DP left-to-right loop: dp[w] = max(dp[w], val + dp[w-wt])medium
- 13.Rod CuttingUnbounded max value DP with piece lengths as item weightsmedium
- 14.Minimum Cost to Fill Given WeightUnbounded min cost DP with exact weight capacity targetmedium
- 15.Count Subsets With Given Sum0/1 counting DP: dp[s] = dp[s] + dp[s - num]medium
- 16.Minimum Subset Sum DifferenceSubset sum reachability up to total/2; min diff = total - 2*S1medium
- 17.Number of Subsets With Given DifferenceSubset sum count for target S1 = (total + diff)/2medium
- 18.Integer BreakUnbounded product maximization DP for integer split partsmedium
- 19.Shopping Offers — related multidimensional stateMultidimensional offer vector Knapsack / Memoized DFSmedium
- 20.Tallest BillboardState difference DP: dp[diff] tracking max lower billboard heighthard
Also Important
8 advanced Knapsack variations worth practicing.
- 21.Multiple KnapsackItem quantities limit DP with binary item decompositionhard
- 22.Bounded KnapsackBinary splitting into 0/1 items (counts 1, 2, 4...) for O(N log K * W)medium
- 23.Group KnapsackChoose at most 1 item per group: outer group loop, inner capacity right-to-leftmedium
- 24.Multi-Dimensional KnapsackMultiple capacity limits (e.g. weight + volume) 2D/3D DP tableshard
- 25.Knapsack with Value-Based DPSwap state: dp[val] = min weight required when total weight W is hugehard
- 26.Meet-in-the-Middle Subset Sum — advancedSplit N into N/2 parts + binary search when N <= 40 and W is hugehard
- 27.Bitset Optimization for Subset SumBitwise shift subset sum: dp |= (dp << num) for 64x speeduphard
- 28.Knapsack Reconstruction — return chosen itemsBacktrack table: compare dp[i][w] == dp[i-1][w] to identify chosen itemsmedium
How to Think
For every item, compare TAKE vs SKIP under remaining capacity.
- Items + limited capacity limit?Knapsack DP
- Every item can be used once?0/1 Knapsack (Right-to-Left loop)
- Item can be reused unlimited times?Unbounded Knapsack (Left-to-Right loop)
- Need only true/false reachability?Subset Sum DP
- Need count of ways / combinations?Counting Knapsack DP
Go Quick Reference — Knapsack
Compilable Go code demonstrating 0/1 Knapsack (Right-to-Left loop) and Unbounded Knapsack (Left-to-Right loop).
package main
import "fmt"
// 0/1 Knapsack: Items used ONCE -> Right-to-Left Capacity Loop
func knapsack01(weights []int, values []int, capacity int) int {
dp := make([]int, capacity+1)
for i := 0; i < len(weights); i++ {
wt, val := weights[i], values[i]
// Loop Backwards to prevent reusing current item i
for w := capacity; w >= wt; w-- {
if val+dp[w-wt] > dp[w] {
dp[w] = val + dp[w-wt]
}
}
}
return dp[capacity]
}
// Unbounded Knapsack: Items REUSED -> Left-to-Right Capacity Loop
func unboundedKnapsack(weights []int, values []int, capacity int) int {
dp := make([]int, capacity+1)
for i := 0; i < len(weights); i++ {
wt, val := weights[i], values[i]
// Loop Forwards to allow reusing current item i multiple times
for w := wt; w <= capacity; w++ {
if val+dp[w-wt] > dp[w] {
dp[w] = val + dp[w-wt]
}
}
}
return dp[capacity]
}
func main() {
weights := []int{2, 3, 4}
values := []int{6, 10, 12}
cap := 5
fmt.Println("0/1 Max Value:", knapsack01(weights, values, cap)) // Output: 16 (Items 2+3)
fmt.Println("Unbounded Max Value:", unboundedKnapsack(weights, values, cap)) // Output: 16
}👉 Knapsack: O(N × W) time and O(W) 1D array space.
Core Knapsack Mechanics & Loop Directions
0/1 vs Unbounded 1D Update Rule
• 0/1 Knapsack (Use Once): Loop capacity w = W down to wt (Right-to-Left) so cell reads previous iteration values.
• Unbounded Knapsack (Reuse): Loop capacity w = wt up to W (Left-to-Right) so cell reads newly updated values.
Subset Sum & Target Sum Transformation
• Subset Sum: Boolean reachability dp[s] = dp[s] || dp[s - num].
• Target Sum: Transform positive subset requirement: P = (total + target) / 2, then count subsets equal to P.
Define dp[i][w] State Meaning → Compare Take vs Skip Choice
0/1 Knapsack -> Capacity Loop Right-to-Left (Backwards)
Unbounded Knapsack -> Capacity Loop Left-to-Right (Forwards)
Combinations Count -> for coin in coins { for amount in coin..W }
Permutations Count -> for amount in 1..W { for coin in coins }
Pseudo-Polynomial Time -> O(N * Capacity); W = 10^9 requires value-based DP or meet-in-middleKnapsack Variants & Loop Ordering
Combinations vs Permutations Loop Order
• Coin Change II (Combinations): Outer coin loop, inner amount loop (for c in coins { for a in c..W }). Order ignored ({1,2} == {2,1}).
• Combination Sum IV (Permutations): Outer amount loop, inner coin loop (for a in 1..W { for c in coins }). Order matters ({1,2} != {2,1}).
Multi-Dimensional & Value-Based DP
• Multi-Dimensional Knapsack (Ones & Zeroes): 2D capacity table dp[zeros][ones] updated backwards.
• Value-Based DP: When capacity W is massive (e.g. 10⁹) but max value is small, swap state: dp[val] = min weight required.
Knapsack Item Selection Reconstruction
To recover chosen items from 2D DP matrix dp[N][W]:
Backtrack from i = N, w = W:
• If dp[i][w] == dp[i-1][w]: Item i was SKIPPED.
• Else: Item i was TAKEN -> record item i and set w -= weight[i].
Common Interview Mistakes
- • Failing to identify 0/1 vs Unbounded item reuse rules before choosing capacity loop directions.
- • Running 0/1 Knapsack 1D capacity loop forwards (accidentally allowing item reuse).
- • Using 0 base initialization for counting/boolean problems instead of dp[0] = 1 or dp[0] = true.
- • Swapping loop order in Coin Change II (accidentally counting permutations instead of combinations).
- • Updating DP values when item weight > current capacity w (item does not fit).
- • Assuming Knapsack complexity is always polynomial (it is pseudo-polynomial in capacity W).
- • Attempting full capacity 1D DP when W = 10^9 (causes memory overflow; requires value-based DP or meet-in-middle).
- • Confusing subset selection (unordered item choices) with subsequence ordering.
- • Failing to check odd total sum in Partition Equal Subset Sum (odd sum can never be halved into integers).
Interview Rules
Item choices + capacity limit? → Knapsack DP
Rule 1 → Define dp[i][w] state meaning explicitly
Rule 2 → 0/1 (Use once) = 1D Capacity Loop Right-to-Left
Rule 3 → Unbounded (Reuse) = 1D Capacity Loop Left-to-Right
Rule 4 → Subset Sum = Boolean reachability dp[0] = true
Rule 5 → Combinations count = Outer coin loop; Permutations count = Outer amount loop
Rule 6 → Massive capacity W = 10^9? Use Value-based DP or Meet-in-middle
Time Complexity → O(N * Capacity) (Pseudo-Polynomial)
Space Complexity → O(N * Capacity) 2D Table or O(Capacity) 1D Array💡 Golden Rule: For each item, compare taking it against skipping it, while keeping enough capacity state to prevent using more resources than allowed.
Production Thinking
Budget Allocation Systems → Knapsack models financial spending limits across competing project proposals.
Server Resource & CPU Scheduling → multi-capacity Knapsack optimizes job scheduling under combined CPU and RAM thresholds.
Storage & Feature Selection → storage-constrained feature packaging selects maximum-value payloads under payload byte caps.
Production warning → capacity W scaling can explode table size; estimate bytes = W × 8 to prevent Memory Limit Exceeded crashes.
Remember This
Knapsack Core: TAKE vs SKIP decision
0/1 (Use Once): Capacity Loop Right-to-Left (w = W down to wt)
Unbounded (Reuse): Capacity Loop Left-to-Right (w = wt up to W)
Subset Sum Target: P = (total + target) / 2
Combinations: for coin { for amount }
Permutations: for amount { for coin }
Pseudo-Polynomial: Time = O(N * W), Space = O(W)