← Back to DSA Course | Chapter 14: Dynamic Programming | Lesson 4 of 12

0-1 Knapsack

0-1 knapsack is like packing a bag where each item is either taken or left behind, choosing the mix that gives the most value without going over the weight limit.
Syntax
markup
dp = [[0] * (W + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
    for w in range(W + 1):
        dp[i][w] = dp[i - 1][w]                      # skip item
        if weight[i - 1] <= w:
            dp[i][w] = max(dp[i][w], value[i - 1] + dp[i - 1][w - weight[i - 1]])   # take item

Problem Idea

In the 0-1 knapsack problem, you're choosing which items to pack into a bag with a fixed weight capacity to maximize total value, but each item is either fully taken or fully left behind — there's no way to take part of one or take it twice.

Example: Problem Idea

#include <iostream>
using namespace std;
int main() {
	int weights[] = {2,3,4}, values[] = {3,4,5}, capacity = 5;
	cout << "Choose subset of items, weight <= " << capacity << ", each item fully taken or fully left";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] weights = {2,3,4}, values = {3,4,5};
		int capacity = 5;
		System.out.println("Choose subset of items, weight <= " + capacity + ", each item fully taken or fully left");
	}
}
weights, values, capacity = [2,3,4], [3,4,5], 5
print(f"Choose subset of items, weight <= {capacity}, each item fully taken or fully left")
#include <stdio.h>
int main() {
	int weights[] = {2,3,4}, values[] = {3,4,5}, capacity = 5;
	printf("Choose subset of items, weight <= %d, each item fully taken or fully left", capacity);
	return 0;
}

DP State

dp[i][w] represents the best total value achievable using only the first i items with a knapsack of capacity w, so building this table item by item eventually reveals the best possible value for every capacity you might need.

Example: DP State

#include <iostream>
using namespace std;
int main() {
	int dp[4][6] = {0};
	cout << "dp[i][w] = best value using first i items with capacity w; dp[3][5] is the final answer slot";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[][] dp = new int[4][6];
		System.out.println("dp[i][w] = best value using first i items with capacity w; dp[3][5] is the final answer slot");
	}
}
dp = [[0]*6 for _ in range(4)]
print("dp[i][w] = best value using first i items with capacity w; dp[3][5] is the final answer slot")
#include <stdio.h>
int main() {
	int dp[4][6] = {0};
	printf("dp[i][w] = best value using first i items with capacity w; dp[3][5] is the final answer slot");
	return 0;
}

Transition

For each item you have exactly two choices: skip it and keep the best value already found without it, or take it (if it fits) and add its value to the best result for the remaining capacity — the DP transition simply takes the better of these two options.

Example: Transition

#include <iostream>
using namespace std;
int main() {
	int weights[] = {2,3,4}, values[] = {3,4,5}, capacity = 5, n = 3;
	int dp[4][6] = {0};
	for (int i = 1; i <= n; i++)
		for (int w = 0; w <= capacity; w++) {
			dp[i][w] = dp[i-1][w];
			if (weights[i-1] <= w) dp[i][w] = max(dp[i][w], dp[i-1][w-weights[i-1]] + values[i-1]);
		}
	cout << "Best value with capacity " << capacity << ": " << dp[n][capacity];
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] weights = {2,3,4}, values = {3,4,5};
		int capacity = 5, n = 3;
		int[][] dp = new int[4][6];
		for (int i = 1; i <= n; i++)
			for (int w = 0; w <= capacity; w++) {
				dp[i][w] = dp[i-1][w];
				if (weights[i-1] <= w) dp[i][w] = Math.max(dp[i][w], dp[i-1][w-weights[i-1]] + values[i-1]);
			}
		System.out.println("Best value with capacity " + capacity + ": " + dp[n][capacity]);
	}
}
weights, values, capacity, n = [2,3,4], [3,4,5], 5, 3
dp = [[0]*(capacity+1) for _ in range(n+1)]
for i in range(1, n+1):
    for w in range(capacity+1):
        dp[i][w] = dp[i-1][w]
        if weights[i-1] <= w:
            dp[i][w] = max(dp[i][w], dp[i-1][w-weights[i-1]] + values[i-1])
print(f"Best value with capacity {capacity}: {dp[n][capacity]}")
#include <stdio.h>
int main() {
	int weights[] = {2,3,4}, values[] = {3,4,5}, capacity = 5, n = 3;
	int dp[4][6] = {0};
	for (int i = 1; i <= n; i++)
		for (int w = 0; w <= capacity; w++) {
			dp[i][w] = dp[i-1][w];
			if (weights[i-1] <= w && dp[i-1][w-weights[i-1]] + values[i-1] > dp[i][w])
				dp[i][w] = dp[i-1][w-weights[i-1]] + values[i-1];
		}
	printf("Best value with capacity %d: %d", capacity, dp[n][capacity]);
	return 0;
}

Space Optimization

Since dp[i][w] only ever depends on the previous row (dp[i-1][...]), you can collapse the 2D table into a single 1D array, as long as you update it from high capacity down to low capacity so you don't accidentally reuse an item twice.

Example: Space Optimization

#include <iostream>
using namespace std;
int main() {
	int weights[] = {2,3,4}, values[] = {3,4,5}, capacity = 5;
	int dp[6] = {0};
	for (int i = 0; i < 3; i++)
		for (int w = capacity; w >= weights[i]; w--)
			dp[w] = max(dp[w], dp[w-weights[i]] + values[i]);
	cout << "1D array updated high-to-low, best value: " << dp[capacity];
	return 0;
}
public class Main {
	public static void main(String[] args) {
		int[] weights = {2,3,4}, values = {3,4,5};
		int capacity = 5;
		int[] dp = new int[6];
		for (int i = 0; i < 3; i++)
			for (int w = capacity; w >= weights[i]; w--)
				dp[w] = Math.max(dp[w], dp[w-weights[i]] + values[i]);
		System.out.println("1D array updated high-to-low, best value: " + dp[capacity]);
	}
}
weights, values, capacity = [2,3,4], [3,4,5], 5
dp = [0]*(capacity+1)
for i in range(3):
    for w in range(capacity, weights[i]-1, -1):
        dp[w] = max(dp[w], dp[w-weights[i]] + values[i])
print("1D array updated high-to-low, best value:", dp[capacity])
#include <stdio.h>
int main() {
	int weights[] = {2,3,4}, values[] = {3,4,5}, capacity = 5;
	int dp[6] = {0};
	for (int i = 0; i < 3; i++)
		for (int w = capacity; w >= weights[i]; w--)
			if (dp[w-weights[i]] + values[i] > dp[w]) dp[w] = dp[w-weights[i]] + values[i];
	printf("1D array updated high-to-low, best value: %d", dp[capacity]);
	return 0;
}

Practice

0-1 knapsack is the classic example for 'choose or don't choose' optimization problems, and the same DP shape reappears whenever you're selecting a subset of items under a hard constraint.

Example: Practice

#include <iostream>
using namespace std;
int main() {
	cout << "Same DP shape reappears anywhere you select a subset of items under a hard constraint";
	return 0;
}
public class Main {
	public static void main(String[] args) {
		System.out.println("Same DP shape reappears anywhere you select a subset of items under a hard constraint");
	}
}
print("Same DP shape reappears anywhere you select a subset of items under a hard constraint")
#include <stdio.h>
int main() {
	printf("Same DP shape reappears anywhere you select a subset of items under a hard constraint");
	return 0;
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Looping the capacity upward in the 1D version, which lets the same item be used many times (that is unbounded knapsack).
  2. Not checking that the item's weight is at most the current capacity before taking it.
  3. Initializing the table with something other than 0, or mixing up dp[i][w] with dp[w][i].

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.