0-1 knapsack Theory


Introduction

Definition: Given n items with weights w[] and values v[], and a knapsack of capacity W, select items to maximize total value without exceeding capacity. Each item is either taken (1) or not taken (0) — binary choice.

0/1 vs. Fractional Knapsack:

Real-World Applications:


Problem Statement

Inputs:

Recursive Relation:

dp[i][w] = max( 
    dp[i-1][w],  // Skip item i
    val[i-1] + dp[i-1][w - wt[i-1]]  // Take item i (if w >= wt[i-1])
)

Approaches


1. Naive Recursion

public class KnapsackRecursive {
    public static int knapSack(int W, int[] wt, int[] val, int n) {
        if (n == 0 || W == 0) return 0;
        if (wt[n-1] > W) 
            return knapSack(W, wt, val, n-1);
        else 
            return Math.max(
                knapSack(W, wt, val, n-1),
                val[n-1] + knapSack(W - wt[n-1], wt, val, n-1)
            );
    }
}

Recursion Tree (n=3, W=4):

                         KS(3,4)
                      /          \
            wt[2]=5 <=4?        KS(2,4)
            (Skip)             /        \
                         KS(1,4)       KS(1,1)
                        /     \           /   \
                   KS(0,4) KS(0,1)   KS(0,1) KS(0,-2)

Redundant calls: KS(1,1) computed twice

Complexity:


2. Memoization (Top-Down DP)

import java.util.Arrays;

public class KnapsackMemoization {
    public static int knapSack(int W, int[] wt, int[] val, int n, int[][] dp) {
        if (n == 0 || W == 0) return 0;
        if (dp[n][W] != -1) return dp[n][W];  // Return cached result
        
        if (wt[n-1] > W) 
            dp[n][W] = knapSack(W, wt, val, n-1, dp);
        else 
            dp[n][W] = Math.max(
                knapSack(W, wt, val, n-1, dp),
                val[n-1] + knapSack(W - wt[n-1], wt, val, n-1, dp)
            );
        return dp[n][W];
    }

    public static void main(String[] args) {
        int[] val = {60, 100, 120};
        int[] wt = {10, 20, 30};
        int W = 50, n = 3;
        int[][] dp = new int[n+1][W+1];
        for (int[] row : dp) Arrays.fill(row, -1);
        System.out.println(knapSack(W, wt, val, n, dp)); // Output: 220
    }
}

Dry Run (n=3, W=50):

Call Stack Action dp[n][W] Result
KS(3,50) max(KS(2,50), 120 + KS(2,20)) 220
├─ KS(2,50) max(KS(1,50), 100 + KS(1,30)) 160
└─ KS(2,20) max(KS(1,20), 100 + KS(1,0)) 100

Complexity:


3. Tabulation (Bottom-Up DP)

public class KnapsackTabulation {
    public static int knapSack(int W, int[] wt, int[] val, int n) {
        int[][] dp = new int[n+1][W+1];
        
        for (int i = 1; i <= n; i++) {
            for (int w = 1; w <= W; w++) {
                if (wt[i-1] <= w) 
                    dp[i][w] = Math.max(
                        dp[i-1][w], 
                        val[i-1] + dp[i-1][w - wt[i-1]]
                    );
                else 
                    dp[i][w] = dp[i-1][w];
            }
        }
        return dp[n][W];
    }
}
DP Table (wt = [1,2,3], val = [10,15,40], W = 5):
i \ w 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 10 10 10 10 10
2 0 10 15 25 25 25
3 0 10 15 40 50 55

Max Value = 55

Cell [3][5] = max(25, 40 + dp[2][2] = 40 + 15 = 55)

4. Space Optimization

Key Insight: Only previous row needed → use 1D array
Use Reverse Iteration: Prevents overwriting values

public static int knapSackOptimized(int W, int[] wt, int[] val, int n) {
    int[] dp = new int[W+1];
    
    for (int i = 0; i < n; i++) {
        for (int w = W; w >= wt[i]; w--) {
            dp[w] = Math.max(dp[w], val[i] + dp[w - wt[i]]);
        }
    }
    return dp[W];
}

Dry Run (val = [60, 100, 120], wt = [10, 20, 30], W = 50):

Iteration dp[50] dp[40] dp[30] dp[20] dp[10]
i = 0 60 60 60 60 60
i = 1 160 160 160 100 60
i = 2 220 180 120 100 60

Complexity Summary

Approach Time Space When to Use
Naive Recursion O(2ⁿ) O(n) Only for very small n
Memoization O(n×W) O(n×W) When stack size isn’t a concern
Tabulation O(n×W) O(n×W) Default go-to for most use cases
Space Optimized O(n×W) O(W) When space is limited

Applications & Variants

Real-World Use Cases:

Common Variants:

Variant Key Difference Recurrence / Change
Unbounded Knapsack Reuse items multiple times Replace dp[i-1] with dp[i]
Subset Sum Check if sum exists Use boolean DP
Equal Partition Subset with sum = total/2 Reduce to subset sum
Multi-dimensional Additional constraints (e.g., weight + volume) → 3D DP table Add more dimensions to DP array

Key Takeaways for Interviews

  1. Identify Knapsack Problems: Look for “max value” + constraint

  2. State Transition: Always frame as pick vs skip

  3. Optimize Space: 1D + reverse iteration for large inputs

  4. Edge Cases:

    • W = 0, all weights > W

    • Negative weights or values (rare in 0/1)

  5. Testing: Use degenerate test cases, boundary cases

Common Pitfalls:

Knapsack is the gateway to DP mastery. Practice variants until you can derive the state transition in your sleep.” — Senior FAANG Interviewer