Code Templates
Master the knapsack problem with these optimized code templates. Use them as starting points for solving various knapsack variations across different programming languages. For a conceptual overview, check out the Knapsack Guide.
When to Use Knapsack Templates
Knapsack algorithms are your go-to solution for optimization and subset selection problems where you need to:
- Maximize value or minimize cost with constraints
- Choose items with weights and values
- Find combinations that meet target sums
- Solve partition, subset sum, or coin change problems
Key Characteristics:
- Discrete choices (take or don’t take) for 0/1 knapsack
- Unlimited repetition allowed for unbounded knapsack
- time withO(N*W)orO(N*W)spaceO(W)
- Optimal substructure - optimal solution contains optimal solutions to subproblems
Common Applications:
- Resource allocation problems
- Budget optimization
- Subset sum and partition problems
- Coin change and combination counting
- Multi-constraint selection (ones and zeroes)
0/1 Knapsack - Maximize Value
The classic 0/1 knapsack problem where each item can be used at most once. This is the foundation for all knapsack variations.
When to Use:
- Items have weights and values
- Each item can be chosen at most once
- Need to maximize total value within capacity constraint
- Subset selection problems
Key Insight: dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight] + value) - either exclude or include the current item.
JavaScript:
/**
* 0/1 Knapsack - Maximize value with weight constraint
* @param {number[]} weights - Array of item weights
* @param {number[]} values - Array of item values
* @param {number} capacity - Knapsack capacity
* @returns {number} Maximum value achievable
*/
function knapsack01(weights, values, capacity) {
const n = weights.length;
const dp = Array(n + 1).fill().map(() => Array(capacity + 1).fill(0));
for (let i = 1; i <= n; i++) {
for (let w = 0; w <= capacity; w++) {
if (weights[i - 1] <= w) {
dp[i][w] = Math.max(
dp[i - 1][w], // Exclude item
values[i - 1] + dp[i - 1][w - weights[i - 1]] // Include item
);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][capacity];
}Python:
from typing import List
def knapsack_01(weights: List[int], values: List[int], capacity: int) -> int:
n = len(weights)
dp = [[0] * (capacity + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(capacity + 1):
if weights[i - 1] <= w:
dp[i][w] = max(
dp[i - 1][w], # Exclude item
values[i - 1] + dp[i - 1][w - weights[i - 1]] # Include item
)
else:
dp[i][w] = dp[i - 1][w]
return dp[n][capacity]Java:
public class Knapsack {
/**
* 0/1 Knapsack - Maximize value with weight constraint
*/
public int knapsack01(int[] weights, int[] values, int capacity) {
int n = weights.length;
int[][] dp = new int[n + 1][capacity + 1];
for (int i = 1; i <= n; i++) {
for (int w = 0; w <= capacity; w++) {
if (weights[i - 1] <= w) {
dp[i][w] = Math.max(
dp[i - 1][w], // Exclude item
values[i - 1] + dp[i - 1][w - weights[i - 1]] // Include item
);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][capacity];
}
}C++:
#include <vector>
#include <algorithm>
class Knapsack {
public:
/**
* 0/1 Knapsack - Maximize value with weight constraint
*/
int knapsack01(const std::vector<int>& weights,
const std::vector<int>& values,
int capacity) {
int n = weights.size();
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(capacity + 1, 0));
for (int i = 1; i <= n; i++) {
for (int w = 0; w <= capacity; w++) {
if (weights[i - 1] <= w) {
dp[i][w] = std::max(
dp[i - 1][w], // Exclude item
values[i - 1] + dp[i - 1][w - weights[i - 1]] // Include item
);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
return dp[n][capacity];
}
};Go:
// 0/1 Knapsack - Maximize value with weight constraint
func Knapsack01(weights, values []int, capacity int) int {
n := len(weights)
dp := make([][]int, n+1)
for i := range dp {
dp[i] = make([]int, capacity+1)
}
for i := 1; i <= n; i++ {
for w := 0; w <= capacity; w++ {
if weights[i-1] <= w {
dp[i][w] = max(
dp[i-1][w], // Exclude item
values[i-1]+dp[i-1][w-weights[i-1]], // Include item
)
} else {
dp[i][w] = dp[i-1][w]
}
}
}
return dp[n][capacity]
}Ruby:
# 0/1 Knapsack - Maximize value with weight constraint
def knapsack_01(weights, values, capacity)
n = weights.length
dp = Array.new(n + 1) { Array.new(capacity + 1, 0) }
(1..n).each do |i|
(0..capacity).each do |w|
if weights[i - 1] <= w
dp[i][w] = [
dp[i - 1][w], # Exclude item
values[i - 1] + dp[i - 1][w - weights[i - 1]] # Include item
].max
else
dp[i][w] = dp[i - 1][w]
end
end
end
dp[n][capacity]
end0/1 Knapsack - Space Optimized
Optimized version using 1D array instead of 2D table. Reduces space complexity from
Key Change: Iterate backwards on capacity to prevent reusing the same item multiple times.
JavaScript:
/**
* Space-optimized 0/1 knapsack using 1D array
*/
function knapsack01Optimized(weights, values, capacity) {
const dp = new Array(capacity + 1).fill(0);
for (let i = 0; i < weights.length; i++) {
// Iterate backwards to avoid using same item twice
for (let w = capacity; w >= weights[i]; w--) {
dp[w] = Math.max(dp[w], values[i] + dp[w - weights[i]]);
}
}
return dp[capacity];
}Python:
def knapsack_01_optimized(weights: list[int], values: list[int], capacity: int) -> int:
dp = [0] * (capacity + 1)
for i in range(len(weights)):
# Iterate backwards to avoid using same item twice
for w in range(capacity, weights[i] - 1, -1):
dp[w] = max(dp[w], values[i] + dp[w - weights[i]])
return dp[capacity]Java:
public int knapsack01Optimized(int[] weights, int[] values, int capacity) {
int[] dp = new int[capacity + 1];
for (int i = 0; i < weights.length; i++) {
// Iterate backwards to avoid using same item twice
for (int w = capacity; w >= weights[i]; w--) {
dp[w] = Math.max(dp[w], values[i] + dp[w - weights[i]]);
}
}
return dp[capacity];
}C++:
int knapsack01Optimized(const std::vector<int>& weights,
const std::vector<int>& values,
int capacity) {
std::vector<int> dp(capacity + 1, 0);
for (size_t i = 0; i < weights.size(); i++) {
// Iterate backwards to avoid using same item twice
for (int w = capacity; w >= weights[i]; w--) {
dp[w] = std::max(dp[w], values[i] + dp[w - weights[i]]);
}
}
return dp[capacity];
}Go:
// Space-optimized 0/1 knapsack using 1D array
func Knapsack01Optimized(weights, values []int, capacity int) int {
dp := make([]int, capacity+1)
for i := 0; i < len(weights); i++ {
// Iterate backwards to avoid using same item twice
for w := capacity; w >= weights[i]; w-- {
dp[w] = max(dp[w], values[i]+dp[w-weights[i]])
}
}
return dp[capacity]
}Ruby:
# Space-optimized 0/1 knapsack using 1D array
def knapsack_01_optimized(weights, values, capacity)
dp = Array.new(capacity + 1, 0)
weights.each_with_index do |weight, i|
# Iterate backwards to avoid using same item twice
(capacity).downto(weight).each do |w|
dp[w] = [dp[w], values[i] + dp[w - weight]].max
end
end
dp[capacity]
endUnbounded Knapsack - Count Ways (Coin Change 2)
Count the number of ways to make up a target amount using unlimited coins.
When to Use:
- Unlimited quantity of each item
- Count combinations, not just maximum value
- Coin change, combination problems
Key Insight: For each coin, update dp from coin to amount. Iterate forward to allow unlimited use.
JavaScript:
/**
* Count combinations to make amount (Coin Change 2)
* @param {number[]} coins - Coin denominations
* @param {number} amount - Target amount
* @returns {number} Number of combinations
*/
function coinChange2(coins, amount) {
const dp = new Array(amount + 1).fill(0);
dp[0] = 1; // One way to make 0
for (const coin of coins) {
for (let i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}Python:
from typing import List
def coin_change_2(coins: List[int], amount: int) -> int:
dp = [0] * (amount + 1)
dp[0] = 1 # One way to make 0
for coin in coins:
for i in range(coin, amount + 1):
dp[i] += dp[i - coin]
return dp[amount]Java:
public class CoinChange {
/**
* Count combinations to make amount
*/
public int coinChange2(int[] coins, int amount) {
int[] dp = new int[amount + 1];
dp[0] = 1; // One way to make 0
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
}C++:
#include <vector>
class CoinChange {
public:
int coinChange2(const std::vector<int>& coins, int amount) {
std::vector<int> dp(amount + 1, 0);
dp[0] = 1; // One way to make 0
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
};Go:
// Count combinations to make amount (Coin Change 2)
func CoinChange2(coins []int, amount int) int {
dp := make([]int, amount+1)
dp[0] = 1 // One way to make 0
for _, coin := range coins {
for i := coin; i <= amount; i++ {
dp[i] += dp[i-coin]
}
}
return dp[amount]
}Ruby:
# Count combinations to make amount (Coin Change 2)
def coin_change_2(coins, amount)
dp = Array.new(amount + 1, 0)
dp[0] = 1 # One way to make 0
coins.each do |coin|
(coin..amount).each do |i|
dp[i] += dp[i - coin]
end
end
dp[amount]
endUnbounded Knapsack - Minimum Coins (Coin Change 1)
Find the minimum number of coins needed to make up a target amount.
When to Use:
- Minimize number of items to reach target
- Unlimited quantity of each item available
- Return -1 if impossible
Key Insight: Initialize dp with Infinity (except dp[0] = 0). Use min instead of max.
JavaScript:
/**
* Minimum coins to make amount (Coin Change 1)
* @param {number[]} coins - Coin denominations
* @param {number} amount - Target amount
* @returns {number} Minimum coins or -1 if impossible
*/
function coinChange(coins, amount) {
const dp = new Array(amount + 1).fill(Infinity);
dp[0] = 0; // Base case
for (const coin of coins) {
for (let i = coin; i <= amount; i++) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
return dp[amount] === Infinity ? -1 : dp[amount];
}Python:
from typing import List
def coin_change(coins: List[int], amount: int) -> int:
dp = [float('inf')] * (amount + 1)
dp[0] = 0 # Base case
for coin in coins:
for i in range(coin, amount + 1):
dp[i] = min(dp[i], dp[i - coin] + 1)
return -1 if dp[amount] == float('inf') else dp[amount]Java:
public class CoinChange {
/**
* Minimum coins to make amount
*/
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
for (int i = 1; i <= amount; i++) {
dp[i] = Integer.MAX_VALUE;
}
dp[0] = 0; // Base case
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
if (dp[i - coin] != Integer.MAX_VALUE) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
}
}C++:
#include <vector>
#include <climits>
class CoinChange {
public:
int coinChange(const std::vector<int>& coins, int amount) {
std::vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0; // Base case
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
if (dp[i - coin] != INT_MAX) {
dp[i] = std::min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] == INT_MAX ? -1 : dp[amount];
}
};Go:
import "math"
// Minimum coins to make amount (Coin Change 1)
func CoinChange(coins []int, amount int) int {
dp := make([]int, amount+1)
for i := 1; i <= amount; i++ {
dp[i] = math.MaxInt32
}
dp[0] = 0 // Base case
for _, coin := range coins {
for i := coin; i <= amount; i++ {
if dp[i-coin] != math.MaxInt32 {
dp[i] = min(dp[i], dp[i-coin]+1)
}
}
}
if dp[amount] == math.MaxInt32 {
return -1
}
return dp[amount]
}Ruby:
# Minimum coins to make amount (Coin Change 1)
def coin_change(coins, amount)
dp = Array.new(amount + 1, Float::INFINITY)
dp[0] = 0 # Base case
coins.each do |coin|
(coin..amount).each do |i|
dp[i] = [dp[i], dp[i - coin] + 1].min
end
end
dp[amount] == Float::INFINITY ? -1 : dp[amount]
endMulti-Dimensional Knapsack (Ones and Zeroes)
2D knapsack with two constraints (zeros and ones budget).
When to Use:
- Multiple constraints (weight, volume, count, etc.)
- LeetCode 474: Ones and Zeroes
- Problems with limited resources
JavaScript:
/**
* Multi-dimensional knapsack with two constraints
* @param {string[]} strs - Array of binary strings
* @param {number} m - Zero budget
* @param {number} n - One budget
* @returns {number} Maximum subset size
*/
function findMaxForm(strs, m, n) {
const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(0));
for (const str of strs) {
let zeros = 0, ones = 0;
for (const char of str) {
if (char === '0') zeros++;
else ones++;
}
// Update dp table backwards
for (let i = m; i >= zeros; i--) {
for (let j = n; j >= ones; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1);
}
}
}
return dp[m][n];
}Python:
from typing import List
def find_max_form(strs: List[str], m: int, n: int) -> int:
dp = [[0] * (n + 1) for _ in range(m + 1)]
for s in strs:
zeros = s.count('0')
ones = len(s) - zeros
for i in range(m, zeros - 1, -1):
for j in range(n, ones - 1, -1):
dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1)
return dp[m][n]Java:
public class OnesAndZeroes {
/**
* Multi-dimensional knapsack with two constraints
*/
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
for (String s : strs) {
int zeros = 0, ones = 0;
for (char c : s.toCharArray()) {
if (c == '0') zeros++;
else ones++;
}
for (int i = m; i >= zeros; i--) {
for (int j = n; j >= ones; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1);
}
}
}
return dp[m][n];
}
}C++:
#include <vector>
#include <string>
#include <algorithm>
class OnesAndZeroes {
public:
int findMaxForm(const std::vector<std::string>& strs, int m, int n) {
std::vector<std::vector<int>> dp(m + 1, std::vector<int>(n + 1, 0));
for (const std::string& s : strs) {
int zeros = 0, ones = 0;
for (char c : s) {
if (c == '0') zeros++;
else ones++;
}
for (int i = m; i >= zeros; i--) {
for (int j = n; j >= ones; j--) {
dp[i][j] = std::max(dp[i][j], dp[i - zeros][j - ones] + 1);
}
}
}
return dp[m][n];
}
};Go:
import "strings"
// Multi-dimensional knapsack with two constraints
func FindMaxForm(strs []string, m, n int) int {
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
}
for _, s := range strs {
zeros := strings.Count(s, "0")
ones := len(s) - zeros
for i := m; i >= zeros; i-- {
for j := n; j >= ones; j-- {
dp[i][j] = max(dp[i][j], dp[i-zeros][j-ones]+1)
}
}
}
return dp[m][n]
}Ruby:
# Multi-dimensional knapsack with two constraints
def find_max_form(strs, m, n)
dp = Array.new(m + 1) { Array.new(n + 1, 0) }
strs.each do |s|
zeros = s.count('0')
ones = s.length - zeros
(m).downto(zeros).each do |i|
(n).downto(ones).each do |j|
dp[i][j] = [dp[i][j], dp[i - zeros][j - ones] + 1].max
end
end
end
dp[m][n]
endCode Breakdown
The 0/1 knapsack template relies on these key components:
1. The DP Table (dp)
- 2D array of size
(N+1) x (W+1)where N = number of items, W = capacity dp[0][w] = 0for all w - no items available means zero valuedp[i][w]represents maximum value using firstiitems with capacityw
2. State Transition Formula
dp[i][w] = max(
dp[i-1][w], // Exclude item i-1
values[i-1] + dp[i-1][w-weights[i-1]] // Include item i-1
)- Exclude: Same value as previous row without this item
- Include: Item value + best value for remaining capacity from previous row
3. Iteration Direction
graph LR
subgraph Items[Process Items]
A[Item 1] --> B[Item 2] --> C[Item N]
end
subgraph ZeroOne[0/1 Knapsack]
Dir01[W to weight]
Result01[Use item once]
end
subgraph Unbounded[Unbounded Knapsack]
DirUnbounded[weight to W]
ResultUnbounded[Reuse allowed]
end
Items --> ZeroOne
Items --> Unbounded
style Dir01 fill:#aaf,stroke:#333
style DirUnbounded fill:#f9d,stroke:#333
4. Space Optimization
- 2D to 1D: Instead of storing full table, use single array of size
W+1 - Backward iteration: For 0/1 knapsack, iterate from
Wdown toweight[i]- This prevents using the same item multiple times
- When iterating forward,
dp[w - weight[i]]may already include current item
- Forward iteration: For unbounded knapsack, iterate from
weight[i]up toW- Allows unlimited reuse of the same item
Next Steps
Now that you have templates, it’s time to apply them. Head over to the Practice Problems page to solve real LeetCode challenges using these patterns.