Knapsack Problems

The knapsack family asks how to pack items with weights and values into a limited capacity. This lesson covers 0/1 knapsack (each item once), subset sum / partition (LeetCode 416), space-optimized 1D DP, and links to unbounded knapsack — with C, Java, and Python code and output trace tables.

Overview

Knapsack problems differ by whether each item can be used once, unlimited times, or a bounded count.

Variant Reuse items? Typical goal LeetCode
0/1 KnapsackEach item onceMax value in capacity WClassic / 1049
Subset sumEach number onceCan sum to target?416
UnboundedUnlimited copiesMax value or min countCoin change
BoundedLimited copiesMax valueMulti-knapsack variants
0/1 Knapsack recurrence (max value):
  dp[i][w] = max(
      dp[i-1][w],                          // skip item i
      dp[i-1][w - weight[i]] + value[i]     // take item i (if fits)
  )
1D optimized: loop capacity W downward when processing each item

0/1 Knapsack — Max Value

Problem: Given weights, values, and capacity W, maximize total value without exceeding W. Each item at most once.

State: dp[w] = max value achievable with capacity exactly up to w (using items processed so far).

Complexity: O(n × W) time, O(W) space with 1D array.

0/1 Knapsack Code (C, Java, Python)

Keywords Types Functions Variables Strings Numbers Comments

Python

def knapsack_01(weights, values, capacity):
    """0/1 knapsack — max value, each item used at most once. O(n × W)."""
    n = len(weights)
    dp = [0] * (capacity + 1)

    for i in range(n):
        wt, val = weights[i], values[i]
        # Descending w: each item used at most once in 1D DP
        for w in range(capacity, wt - 1, -1):
            dp[w] = max(dp[w], dp[w - wt] + val)

    return dp[capacity]


if __name__ == "__main__":
    w, v, cap = [1, 2, 3], [6, 10, 12], 5
    print(f"weights={w}, values={v}, capacity={cap} → {knapsack_01(w, v, cap)}")
    # items 2+3 (wt 2+3=5, val 10+12=22)

Java

public class Knapsack01 {

    /** Max value 0/1 knapsack with 1D space optimization. */
    public static int knapsack(int[] weights, int[] values, int capacity) {
        int[] dp = new int[capacity + 1];

        for (int i = 0; i < weights.length; i++) {
            int wt = weights[i], val = values[i];
            for (int w = capacity; w >= wt; w--) {
                dp[w] = Math.max(dp[w], dp[w - wt] + val);
            }
        }
        return dp[capacity];
    }

    public static void main(String[] args) {
        int[] w = {1, 2, 3}, v = {6, 10, 12};
        System.out.println(knapsack(w, v, 5));  // 22
    }
}

C

#include <stdio.h>

#define MAX_W 1000

/* 0/1 knapsack — return max value */
int knapsack_01(int weights[], int values[], int n, int capacity) {
    int dp[MAX_W + 1] = {0};
    int i, w, wt, val;

    for (i = 0; i < n; i++) {
        wt = weights[i];
        val = values[i];
        for (w = capacity; w >= wt; w--) {
            int candidate = dp[w - wt] + val;
            if (candidate > dp[w]) dp[w] = candidate;
        }
    }
    return dp[capacity];
}

int main(void) {
    int w[] = {1, 2, 3}, v[] = {6, 10, 12};
    printf("%d\n", knapsack_01(w, v, 3, 5));  /* 22 */
    return 0;
}

0/1 Knapsack Output & Trace

Program output

Language Input Output Explanation
Pythonw=[1,2,3], v=[6,10,12], cap=522Take items 2 and 3 (weights 2+3)
Javasame22Identical logic
Csame22Printed with printf

DP trace — capacity 0…5 after all items

Capacity w dp[w] (max value) Best selection Explanation
00noneEmpty knapsack
16item 1After item 1 (wt=1, val=6)
210item 2Item 2 alone beats 6+…
3161+26+10 from items 1 and 2
4181+36+12
5222+310+12 → final answer

Subset Sum & Partition

Partition Equal Subset Sum (LeetCode 416): Can array be split into two subsets with equal sum?

Equivalent to: does any subset sum to total / 2?

Recurrence: dp[s] = dp[s] OR dp[s − num] — boolean knapsack, each number once.

Subset Sum Code (C, Java, Python)

Python

def can_partition(nums):
    """LeetCode 416 — partition into two equal-sum subsets."""
    total = sum(nums)
    if total % 2 != 0:
        return False  # odd total → impossible

    target = total // 2
    dp = [False] * (target + 1)
    dp[0] = True  # sum 0 always reachable (empty subset)

    for num in nums:
        for s in range(target, num - 1, -1):
            dp[s] = dp[s] or dp[s - num]

    return dp[target]


if __name__ == "__main__":
    print(can_partition([1, 5, 11, 5]))   # True  (11 | 1+5+5)
    print(can_partition([1, 2, 3, 5]))    # False (total=11)

Java

public class SubsetSumPartition {

    public static boolean canPartition(int[] nums) {
        int total = 0;
        for (int x : nums) total += x;
        if (total % 2 != 0) return false;

        int target = total / 2;
        boolean[] dp = new boolean[target + 1];
        dp[0] = true;

        for (int num : nums) {
            for (int s = target; s >= num; s--) {
                dp[s] = dp[s] || dp[s - num];
            }
        }
        return dp[target];
    }

    public static void main(String[] args) {
        System.out.println(canPartition(new int[]{1, 5, 11, 5}));  // true
        System.out.println(canPartition(new int[]{1, 2, 3, 5}));   // false
    }
}

C

#include <stdio.h>
#include <stdbool.h>

#define MAX_SUM 10000

bool can_partition(int nums[], int n) {
    int total = 0, i, num, s;
    for (i = 0; i < n; i++) total += nums[i];
    if (total % 2 != 0) return false;

    int target = total / 2;
    bool dp[MAX_SUM + 1] = {false};
    dp[0] = true;

    for (i = 0; i < n; i++) {
        num = nums[i];
        for (s = target; s >= num; s--) {
            if (dp[s - num]) dp[s] = true;
        }
    }
    return dp[target];
}

int main(void) {
    int a[] = {1, 5, 11, 5}, b[] = {1, 2, 3, 5};
    printf("%d\n", can_partition(a, 4));  /* 1 = true */
    printf("%d\n", can_partition(b, 4));  /* 0 = false */
    return 0;
}

Subset Sum Output & Trace

Program output

Language Input Output Explanation
Python[1, 5, 11, 5]TrueSubset {11} and {1,5,5} both sum to 11
Python[1, 2, 3, 5]FalseTotal 11 — no subset sums to 5.5
Java / Csametrue / false (1/0)Same boolean logic

DP trace — nums = [1, 5, 11, 5], target = 11

After processing Reachable sums (sample) dp[11]? Explanation
Init{0}FalseOnly empty subset
+1{0, 1}FalseCan make sum 1
+5{0,1,5,6}FalseAdd 5 to prior sums
+11… includes 11True11 reached directly
+5… includes 11TrueStill reachable → answer True

Space Optimization

2D knapsack dp[i][w] can compress to 1D dp[w] when items are processed sequentially.

Variant Inner loop on w Why
0/1 KnapsackDescending (W → wt)Prevent reusing same item in one pass
UnboundedAscending (wt → W)Allow multiple uses of same item
Subset sumDescendingEach number once — same as 0/1
Space: 2D table uses O(n × W). 1D rolling array reduces to O(W). Cannot drop below O(W) if capacity is the state dimension.

Knapsack Variants

Problem DP type Time Link
Target sum / +/- signs0/1 countO(n × sum)LeetCode 494
Last stone weight IIPartition min diffO(n × sum)LeetCode 1049
Coin change minUnbounded minO(A × C)Coin change
Profit in job scheduling0/1 + sortO(n log n)Weighted interval

More Examples

Example 1: 0/1 knapsack — no item fits together

weightsvaluescapacityOutputBest pick
[4, 5, 6][10, 12, 15]410Only item 1 fits

Example 2: Partition — odd total

numstotalOutputWhy
[1, 2, 3]6True{1,2} and {3}
[2, 2, 3]7FalseOdd total — immediate reject

Example 3: Complexity

ProblemTimeSpace (optimized)Type
0/1 max valueO(nW)O(W)Pseudo-polynomial
Subset sumO(n × target)O(target)Boolean DP
Naive recursionO(2ⁿ)O(n) stackTry all subsets

Approach Comparison

Approach Time Space When to use
Brute force subsetsO(2ⁿ)O(n)n ≤ 20 only
2D DP tableO(nW)O(nW)Whiteboard clarity
1D DP optimizedO(nW)O(W)Interview final code
Top-down memoO(nW)O(nW) + stackQuick recursive draft

Real-Life Applications

Knapsack and subset-sum patterns appear whenever you have a fixed capacity and must pick a subset of items—each used at most once—to maximize value or reach an exact target.

Domain Real scenario Knapsack mapping Variant
LogisticsLoading a truck or cargo plane with weight/volume limitsItems = packages; weight = capacity; value = shipping revenue or priority0/1 max value
FinancePortfolio selection under a budget capItems = investments; cost = price; value = expected return0/1 max value
Cloud / DevOpsPacking VMs or containers onto a host with CPU/RAM limitsItems = workloads; weight = resource usage; value = SLA priority0/1 or multi-dimensional
Project managementChoosing features for a release with fixed dev hoursItems = features; weight = effort; value = business impact0/1 max value
IT / backupSelecting files to back up within storage quotaItems = files; weight = size; value = criticality score0/1 max value
ManufacturingCutting raw material into parts with minimal wasteItems = part lengths; capacity = stock length; goal = fit subsetSubset sum / bin packing
Load balancingSplitting workloads evenly across two serversCan subset A reach exactly half of total load?Partition / subset sum
RecruitmentForming a team with limited headcount and skill slotsItems = candidates; weight = slots used; value = team score0/1 with constraints
Interview link: When an interviewer asks “select projects under budget” or “can you split this array into two equal sums?”, they are testing the same DP skeleton you learned here—define capacity, loop items, and respect the 0/1 rule (each item once).

Interview Tips

  1. Ask: max value, exact sum, count ways, or partition?
  2. Define dp[w] or dp[i][w] clearly before coding.
  3. For 0/1, emphasize descending capacity loop in 1D form.
  4. Subset sum: check odd total first for partition problems.
  5. State pseudo-polynomial complexity O(n × W).
  6. Connect unbounded variant to coin change.
  7. Trace small example: 2 items, capacity 3, on whiteboard.

Key Takeaway

0/1 knapsack maximizes value with each item once — loop capacity downward in 1D DP. Subset sum / partition is the same skeleton with boolean states and target sum/2. Master the loop direction: descending = 0/1, ascending = unbounded.

Next up: Longest increasing subsequence · Coin change problems · DP properties & complexity