Skip to content
advancedPhase 6 · Dynamic Programming

Knapsack

Master 0/1 knapsack and its variants for subset sum and partition problems.

1h 15m
5 problems
Topic Progress0%

0/1 Knapsack Problem

The 0/1 Knapsack problem: Given items with weights and values, select items to maximize value without exceeding weight capacity. Each item can be chosen at most once.

Problem Statement

Given:
- n items, each with weight w[i] and value v[i]
- knapsack capacity W

Find: Maximum total value without exceeding capacity W

Recursive Solution

// Time: O(2^n), Space: O(n)
public int knapsackRecursive(int[] wt, int[] val, int W, int n) {
    if (n == 0 || W == 0) return 0;
    
    // If current item's weight > remaining capacity, skip it
    if (wt[n - 1] > W) {
        return knapsackRecursive(wt, val, W, n - 1);
    }
    
    // Max of: (include item) vs (exclude item)
    return Math.max(
        val[n - 1] + knapsackRecursive(wt, val, W - wt[n - 1], n - 1),
        knapsackRecursive(wt, val, W, n - 1)
    );
}

Memoization (Top-Down)

public int knapsackMemo(int[] wt, int[] val, int W, int n, int[][] memo) {
    if (n == 0 || W == 0) return 0;
    if (memo[n][W] != -1) return memo[n][W];
    
    if (wt[n - 1] > W) {
        memo[n][W] = knapsackMemo(wt, val, W, n - 1, memo);
    } else {
        memo[n][W] = Math.max(
            val[n - 1] + knapsackMemo(wt, val, W - wt[n - 1], n - 1, memo),
            knapsackMemo(wt, val, W, n - 1, memo)
        );
    }
    
    return memo[n][W];
}

// Usage:
int[][] memo = new int[n + 1][W + 1];
for (int[] row : memo) Arrays.fill(row, -1);
int result = knapsackMemo(wt, val, W, n, memo);

Tabulation (Bottom-Up)

public int knapsackTab(int[] wt, int[] val, int W, int n) {
    int[][] dp = new int[n + 1][W + 1];
    
    // Base case: 0 items or 0 capacity = 0 value
    for (int i = 0; i <= n; i++) dp[i][0] = 0;
    for (int j = 0; j <= W; j++) dp[0][j] = 0;
    
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= W; j++) {
            if (wt[i - 1] <= j) {
                // Can include item i: max(include, exclude)
                dp[i][j] = Math.max(
                    val[i - 1] + dp[i - 1][j - wt[i - 1]],
                    dp[i - 1][j]
                );
            } else {
                // Cannot include item i
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    
    return dp[n][W];
}

Space-Optimized (1D Array)

// O(n*W) time, O(W) space
public int knapsackOptimized(int[] wt, int[] val, int W, int n) {
    int[] dp = new int[W + 1];
    
    for (int i = 0; i < n; i++) {
        // Traverse backwards to avoid using same item twice
        for (int j = W; j >= wt[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - wt[i]] + val[i]);
        }
    }
    
    return dp[W];
}

// IMPORTANT: Traverse backwards for 0/1 Knapsack!
// Forward traversal = Unbounded Knapsack (can reuse items)

Trace Example

Items: wt=[1,3,4,5], val=[1,4,5,7], W=7

 dp[0..7] after each item:
Initial: [0,0,0,0,0,0,0,0]
Item 1 (w=1,v=1): [0,1,1,1,1,1,1,1]
Item 2 (w=3,v=4): [0,1,1,4,5,5,5,5]
Item 3 (w=4,v=5): [0,1,1,4,5,6,6,9]
Item 4 (w=5,v=7): [0,1,1,4,5,7,8,9]

Answer: dp[7] = 9 (items 2 and 4, or items 1,2,3)

Knapsack Variants and Applications

Unbounded Knapsack

Items can be used unlimited times.

// Only difference: traverse capacity forward
public int unboundedKnapsack(int[] wt, int[] val, int W) {
    int[] dp = new int[W + 1];
    
    for (int i = 0; i < wt.length; i++) {
        for (int j = wt[i]; j <= W; j++) {  // FORWARD traversal!
            dp[j] = Math.max(dp[j], dp[j - wt[i]] + val[i]);
        }
    }
    
    return dp[W];
}

Subset Sum Problem

// Check if subset with given sum exists
public boolean subsetSum(int[] arr, int sum) {
    int n = arr.length;
    boolean[][] dp = new boolean[n + 1][sum + 1];
    
    // Base case: sum 0 is always possible (empty subset)
    for (int i = 0; i <= n; i++) dp[i][0] = true;
    
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= sum; j++) {
            if (arr[i - 1] > j) {
                dp[i][j] = dp[i - 1][j];  // Cannot include
            } else {
                dp[i][j] = dp[i - 1][j] || dp[i - 1][j - arr[i - 1]];
            }
        }
    }
    
    return dp[n][sum];
}

// Space-optimized
public boolean subsetSumOptimized(int[] arr, int sum) {
    boolean[] dp = new boolean[sum + 1];
    dp[0] = true;
    
    for (int num : arr) {
        for (int j = sum; j >= num; j--) {
            dp[j] = dp[j] || dp[j - num];
        }
    }
    
    return dp[sum];
}

Partition Equal Subset Sum

// Can array be partitioned into two subsets with equal sum?
public boolean canPartition(int[] nums) {
    int totalSum = 0;
    for (int num : nums) totalSum += num;
    
    // Odd sum cannot be partitioned equally
    if (totalSum % 2 != 0) return false;
    
    int target = totalSum / 2;
    boolean[] dp = new boolean[target + 1];
    dp[0] = true;
    
    for (int num : nums) {
        for (int j = target; j >= num; j--) {
            dp[j] = dp[j] || dp[j - num];
        }
    }
    
    return dp[target];
}

Coin Change (Minimum Coins)

// Find minimum coins to make amount
public int coinChange(int[] coins, int amount) {
    int[] dp = new int[amount + 1];
    Arrays.fill(dp, amount + 1);  // Initialize with impossible value
    dp[0] = 0;
    
    for (int i = 1; i <= amount; i++) {
        for (int coin : coins) {
            if (coin <= i) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }
    
    return dp[amount] > amount ? -1 : dp[amount];
}

Coin Change II (Number of Ways)

// Find number of combinations to make amount
public int change(int amount, int[] coins) {
    int[] dp = new int[amount + 1];
    dp[0] = 1;
    
    for (int coin : coins) {
        for (int i = coin; i <= amount; i++) {
            dp[i] += dp[i - coin];
        }
    }
    
    return dp[amount];
}

Knapsack Pattern Recognition

Problem Type Key Difference
0/1 Knapsack Each item once Traverse capacity backward
Unbounded Knapsack Unlimited items Traverse capacity forward
Subset Sum Boolean (exists?) Use boolean dp
Partition Equal 0/1 with sum/2 target Check total sum first
Coin Change Unbounded + min/count Different initialization

Amazon Relevance

  • Subset Sum / Partition: Resource allocation, load balancing
  • Coin Change: Payment systems, making change
  • Knapsack: Budget optimization, feature selection

Practice Problems

0/1solved
Partition Equal Subset Sum
0/1 Knapsack Variant

Given an integer array nums, return true if you can partition the array into two subsets such that the sum of elements in both subsets is equal.

Example:

Input: nums = [1,5,11,5]

Output: true

The array can be partitioned as [1, 5, 5] and [11]. Both sum to 11.

Optimal Solution — O(n * target) where target = sum/2 time, O(target) space

This is 0/1 Knapsack where target = totalSum/2. If total sum is odd, return false. Otherwise, check if any subset sums to totalSum/2.

class Solution {
    public boolean canPartition(int[] nums) {
        int totalSum = 0;
        for (int num : nums) totalSum += num;
        
        if (totalSum % 2 != 0) return false;
        
        int target = totalSum / 2;
        boolean[] dp = new boolean[target + 1];
        dp[0] = true;
        
        for (int num : nums) {
            for (int j = target; j >= num; j--) {
                dp[j] = dp[j] || dp[j - num];
            }
        }
        
        return dp[target];
    }
}

Edge Cases:

  • Single element: cannot partition into two subsets
  • Two equal elements: can partition
  • All zeros: can partition if at least 2 elements
  • Odd total sum: immediately return false

Quiz

1. What is the key difference between 0/1 Knapsack and Unbounded Knapsack in the DP implementation?

Question 1 options

2. How do you recognize a Knapsack problem in disguise?

Question 2 options

3. What is the primary purpose of Knapsack Problems?

Question 3 options

4. What is a common mistake when implementing Knapsack Problems?

Question 4 options

Flashcards

Question

What is the state transition for 0/1 Knapsack?

Answer

dp[i][w] = max(dp[i-1][w], dp[i-1][w-wt[i]] + val[i]) representing exclude or include item i.

Question

When do you traverse capacity forward vs backward?

Answer

Backward for 0/1 Knapsack (each item once). Forward for Unbounded Knapsack (unlimited items). Forward allows reuse of same item.

Question

What is Knapsack Problems?

Answer

Knapsack Problems is a key concept in software engineering.

Question

When to use Knapsack Problems?

Answer

Use Knapsack Problems when building production systems that require reliability, scalability, and maintainability.

Question

Knapsack Problems best practices

Answer

Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.

Revision Notes

Key Takeaways

  • 1.0/1 Knapsack: backward traversal, each item once
  • 2.Unbounded Knapsack: forward traversal, unlimited items
  • 3.Subset Sum is 0/1 Knapsack with boolean dp
  • 4.Partition Equal = Subset Sum with target = totalSum/2

Interview Tips

  • Ask if items can be reused (0/1 vs Unbounded)
  • Start with 2D solution, then optimize to 1D
  • For partition problems, check if sum is odd first
  • Explain why traversal direction matters

Cheat Sheet

Knapsack Cheat Sheet

0/1 Knapsack:

  • Each item used at most once
  • Traverse capacity BACKWARD
  • dp[j] = max(dp[j], dp[j-wt[i]] + val[i])

Unbounded Knapsack:

  • Items can be reused
  • Traverse capacity FORWARD
  • dp[j] = max(dp[j], dp[j-wt[i]] + val[i])

Subset Sum:

  • Boolean dp: dp[j] = dp[j] || dp[j-num]
  • Target = sum/2 for partition problems

Coin Change:

  • Min coins: dp[i] = min(dp[i], dp[i-coin] + 1)
  • Init with impossible value (amount+1)
  • Count ways: dp[i] += dp[i-coin]

Pattern Recognition:

  • 'Select/choose items' + 'capacity constraint' = Knapsack
  • 'Subset with sum' = Subset Sum
  • 'Partition into equal parts' = 0/1 Knapsack with sum/2