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. 1.0/1 Knapsack
    medium
  2. 2.Partition Equal Subset Sum
    medium
  3. 3.Target Sum
    medium
  4. 4.Subset Sum
    medium
  5. 5.Last Stone Weight II
    medium
  6. 6.Ones and Zeroes
    medium
  7. 7.Profitable Schemes
    hard
  8. 8.Coin Change
    medium
  9. 9.Coin Change II
    medium
  10. 10.Combination Sum IV
    medium
  11. 11.Perfect Squares
    medium
  12. 12.Unbounded Knapsack
    medium
  13. 13.Rod Cutting
    medium
  14. 14.Minimum Cost to Fill Given Weight
    medium
  15. 15.Count Subsets With Given Sum
    medium
  16. 16.Minimum Subset Sum Difference
    medium
  17. 17.Number of Subsets With Given Difference
    medium
  18. 18.Integer Break
    medium
  19. 19.Shopping Offers — related multidimensional state
    medium
  20. 20.Tallest Billboard
    hard

Also Important

8 advanced Knapsack variations worth practicing.

  1. 21.Multiple Knapsack
    hard
  2. 22.Bounded Knapsack
    medium
  3. 23.Group Knapsack
    medium
  4. 24.Multi-Dimensional Knapsack
    hard
  5. 25.Knapsack with Value-Based DP
    hard
  6. 26.Meet-in-the-Middle Subset Sum — advanced
    hard
  7. 27.Bitset Optimization for Subset Sum
    hard
  8. 28.Knapsack Reconstruction — return chosen items
    medium

How to Think

For every item, compare TAKE vs SKIP under remaining capacity.

  1. Items + limited capacity limit?Knapsack DP
  2. Every item can be used once?0/1 Knapsack (Right-to-Left loop)
  3. Item can be reused unlimited times?Unbounded Knapsack (Left-to-Right loop)
  4. Need only true/false reachability?Subset Sum DP
  5. 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.

Visual Memory Rule
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-middle

Knapsack 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)