Given a knapsack with a fixed capacity and a set of n items, where each item has an associated value val[i] and weight wt[i], determine the maximum profit that can be obtained by filling the knapsack. Each item can be selected an unlimited number of times.
Examples:
Input: capacity = 100, val[] = [1, 30], wt[] = [1, 50]
Output: 100
Explanation: There are many ways to fill knapsack.
Option 1: 2 instances of 50 unit weight item.
Option 2: 100 instances of 1 unit weight item.
Option 3: 1 instance of 50 unit weight item and 50 instances of 1 unit weight items.
We get maximum value with option 2.
Input: capacity = 8, val[] = [10, 40, 50, 70], wt[] = [1, 3, 4, 5]
Output : 110
Explanation: We get maximum value with one unit of weight 5 and one unit of weight 3.
Table of Content
The idea is to use recursion by breaking the larger problem into smaller subproblems. For each item, we have two choices - either we include the item in our knapsack or we exclude it.
[Naive Approach] Using Recursive Method - O(2^n) time and O(capacity) space
If the item is included, its value is added to the total profit and the problem is solved again for the same item with the remaining capacity reduced by its weight. If the item is excluded, we move to the next item while keeping the remaining capacity unchanged.
This process continues recursively until all items are considered, and the maximum profit is determined by taking the better of the include and exclude choices at each step.
#include <iostream>
#include<vector>
using namespace std;
int knapSackRecur(int i, int capacity, vector<int> &val, vector<int> &wt) {
if (i==val.size()) return 0;
// Consider current item only if its weight is less than equal
// to maximum weight.
int take = 0;
if (wt[i]<=capacity) {
take = val[i] + knapSackRecur(i, capacity-wt[i], val, wt);
}
// Skip the current item
int noTake = knapSackRecur(i+1, capacity, val, wt);
// Return maximum of the two.
return max(take, noTake);
}
int knapSack(int capacity, vector<int> &val, vector<int> &wt) {
return knapSackRecur(0, capacity, val ,wt);
}
int main() {
vector<int> val = {1, 30}, wt = {1, 50};
int capacity = 100;
cout << knapSack(capacity, val, wt);
}
class GfG {
static int knapSackRecur(int i, int capacity, int[] val, int[] wt) {
if (i == val.length) return 0;
// Consider current item only if
// its weight is less than equal
// to maximum weight.
int take = 0;
if (wt[i] <= capacity) {
take = val[i] + knapSackRecur(i, capacity - wt[i], val, wt);
}
// Skip the current item
int noTake = knapSackRecur(i + 1, capacity, val, wt);
// Return maximum of the two.
return Math.max(take, noTake);
}
static int knapSack(int capacity, int[] val, int[] wt) {
return knapSackRecur(0, capacity, val, wt);
}
public static void main(String[] args) {
int[] val = {1, 30};
int[] wt = {1, 50};
int capacity = 100;
System.out.println(knapSack(capacity, val, wt));
}
}
def knapSackRecur(i, capacity, val, wt):
if i == len(val):
return 0
# Consider current item only if
# its weight is less than equal
# to maximum weight.
take = 0
if wt[i] <= capacity:
take = val[i] + knapSackRecur(i, capacity - wt[i], val, wt)
# Skip the current item
noTake = knapSackRecur(i + 1, capacity, val, wt)
# Return maximum of the two.
return max(take, noTake)
def knapSack(capacity, val, wt):
return knapSackRecur(0, capacity, val, wt)
if __name__ == "__main__":
val = [1, 30]
wt = [1, 50]
capacity = 100
print(knapSack(capacity, val, wt))
using System;
class GfG {
static int knapSackRecur(int i, int capacity, int[] val, int[] wt) {
if (i == val.Length) return 0;
// Consider current item only if
// its weight is less than equal
// to maximum weight.
int take = 0;
if (wt[i] <= capacity) {
take = val[i] + knapSackRecur(i, capacity - wt[i], val, wt);
}
// Skip the current item
int noTake = knapSackRecur(i + 1, capacity, val, wt);
// Return maximum of the two.
return Math.Max(take, noTake);
}
static int knapSack(int capacity, int[] val, int[] wt) {
return knapSackRecur(0, capacity, val, wt);
}
static void Main() {
int[] val = {1, 30};
int[] wt = {1, 50};
int capacity = 100;
Console.WriteLine(knapSack(capacity, val, wt));
}
}
function knapSackRecur(i, capacity, val, wt) {
if (i === val.length) return 0;
// Consider current item only if
// its weight is less than equal
// to maximum weight.
let take = 0;
if (wt[i] <= capacity) {
take = val[i] + knapSackRecur(i, capacity - wt[i], val, wt);
}
// Skip the current item
let noTake = knapSackRecur(i + 1, capacity, val, wt);
// Return maximum of the two.
return Math.max(take, noTake);
}
function knapSack(capacity, val, wt) {
return knapSackRecur(0, capacity, val, wt);
}
const val = [1, 30];
const wt = [1, 50];
const capacity = 100;
console.log(knapSack(capacity, val, wt));
Output
100
Using Top-Down DP (Memoization) – O(n*capacity) Time and O(n*capacity) Space
This approach improves the recursive solution by storing the results of already solved subproblems. A 2D DP table is used where each state represents the maximum profit achievable for a given index and remaining capacity. dp[i][j] stores the maximum profit that can be achieved starting from index i with a remaining capacity of j. Before computing the value for a given (i, j) state, the DP table is checked to avoid recomputation. This ensures that each subproblem is solved only once.
#include <iostream>
#include<vector>
using namespace std;
int knapSackRecur(int i, int capacity, vector<int> &val,
vector<int> &wt, vector<vector<int>> &memo) {
if (i == val.size())
return 0;
// If value is memoized.
if (memo[i][capacity] != -1)
return memo[i][capacity];
// Consider current item only if
// its weight is less than equal
// to maximum weight.
int take = 0;
if (wt[i] <= capacity) {
take = val[i] + knapSackRecur(i, capacity - wt[i],
val, wt, memo);
}
// Skip the current item
int noTake = knapSackRecur(i + 1, capacity, val, wt, memo);
// store maximum of the two and return it.
return memo[i][capacity] = max(take, noTake);
}
int knapSack(int capacity, vector<int> &val, vector<int> &wt) {
// 2D matrix for memoization.
vector<vector<int>> memo(val.size(), vector<int>(capacity + 1, -1));
return knapSackRecur(0, capacity, val, wt, memo);
}
int main() {
vector<int> val = {1, 30}, wt = {1, 50};
int capacity = 100;
cout << knapSack(capacity, val, wt);
}
import java.util.Arrays;
class GfG {
static int knapSackRecur(int i, int capacity, int[] val,
int[] wt, int[][] memo) {
if (i == val.length)
return 0;
// If value is memoized.
if (memo[i][capacity] != -1)
return memo[i][capacity];
// Consider current item only if
// its weight is less than equal
// to maximum weight.
int take = 0;
if (wt[i] <= capacity) {
take = val[i]
+ knapSackRecur(i, capacity - wt[i], val, wt,
memo);
}
// Skip the current item
int noTake = knapSackRecur(i + 1, capacity, val, wt, memo);
// store maximum of the two and return it.
return memo[i][capacity] = Math.max(take, noTake);
}
static int knapSack(int capacity, int[] val, int[] wt) {
// 2D matrix for memoization.
int[][] memo = new int[val.length][capacity + 1];
for (int i = 0; i < val.length; i++) {
Arrays.fill(memo[i], -1);
}
return knapSackRecur(0, capacity, val, wt, memo);
}
public static void main(String[] args) {
int[] val = { 1, 30 };
int[] wt = { 1, 50 };
int capacity = 100;
System.out.println(knapSack(capacity, val, wt));
}
}
def knapSackRecur(i, capacity, val, wt, memo):
if i == len(val):
return 0
# If value is memoized.
if memo[i][capacity] != -1:
return memo[i][capacity]
# Consider current item only if
# its weight is less than equal
# to maximum weight.
take = 0
if wt[i] <= capacity:
take = val[i] + knapSackRecur(i, capacity - wt[i], val, wt, memo)
# Skip the current item
noTake = knapSackRecur(i + 1, capacity, val, wt, memo)
# store maximum of the two and return it.
memo[i][capacity] = max(take, noTake)
return memo[i][capacity]
def knapSack(capacity, val, wt):
# 2D matrix for memoization.
memo = [[-1 for _ in range(capacity + 1)] for _ in range(len(val))]
return knapSackRecur(0, capacity, val, wt, memo)
if __name__ == "__main__":
val = [1, 30]
wt = [1, 50]
capacity = 100
print(knapSack(capacity, val, wt))
using System;
class GfG {
static int knapSackRecur(int i, int capacity, int[] val,
int[] wt, int[, ] memo) {
if (i == val.Length)
return 0;
// If value is memoized.
if (memo[i, capacity] != -1)
return memo[i, capacity];
// Consider current item only if
// its weight is less than equal
// to maximum weight.
int take = 0;
if (wt[i] <= capacity) {
take = val[i]
+ knapSackRecur(i, capacity - wt[i], val, wt,
memo);
}
// Skip the current item
int noTake = knapSackRecur(i + 1, capacity, val, wt, memo);
// store maximum of the two and return it.
return memo[i, capacity] = Math.Max(take, noTake);
}
static int knapSack(int capacity, int[] val, int[] wt) {
// 2D matrix for memoization.
int[, ] memo = new int[val.Length, capacity + 1];
for (int i = 0; i < val.Length; i++) {
for (int j = 0; j <= capacity; j++) {
memo[i, j] = -1;
}
}
return knapSackRecur(0, capacity, val, wt, memo);
}
static void Main() {
int[] val = { 1, 30 };
int[] wt = { 1, 50 };
int capacity = 100;
Console.WriteLine(knapSack(capacity, val, wt));
}
}
function knapSackRecur(i, capacity, val, wt, memo) {
if (i === val.length) return 0;
// If value is memoized.
if (memo[i][capacity] !== -1) return memo[i][capacity];
// Consider current item only if
// its weight is less than equal
// to maximum weight.
let take = 0;
if (wt[i] <= capacity) {
take = val[i] + knapSackRecur(i, capacity - wt[i], val, wt, memo);
}
// Skip the current item
let noTake = knapSackRecur(i + 1, capacity, val, wt, memo);
// store maximum of the two and return it.
memo[i][capacity] = Math.max(take, noTake);
return memo[i][capacity];
}
function knapSack(capacity, val, wt) {
// 2D matrix for memoization.
let memo = Array.from({ length: val.length }, () => Array(capacity + 1).fill(-1));
return knapSackRecur(0, capacity, val, wt, memo);
}
const val = [1, 30];
const wt = [1, 50];
const capacity = 100;
console.log(knapSack(capacity, val, wt));
Output
100
Using Bottom-Up DP (Tabulation) – O(n*capacity) Time and O(n*capacity) Space
In this approach, a DP table is built iteratively from smaller capacities to larger ones. Each DP state represents the maximum profit achievable for a given item index and knapsack capacity. Here, dp[i][j] represents the maximum profit obtainable using the first i items with a knapsack capacity of j. The table is filled iteratively by considering whether to include the current item (possibly multiple times, as it is unbounded) or exclude it.
#include <iostream>
#include<vector>
using namespace std;
int knapSack(int capacity, vector<int> &val, vector<int> &wt) {
// 2D matrix for tabulation.
vector<vector<int>> dp(val.size() + 1, vector<int>(capacity + 1, 0));
// Calculate maximum profit for each
// item index and knapsack weight.
for (int i = val.size() - 1; i >= 0; i--) {
for (int j = 1; j <= capacity; j++) {
int take = 0;
if (j - wt[i] >= 0) {
take = val[i] + dp[i][j - wt[i]];
}
int noTake = dp[i + 1][j];
dp[i][j] = max(take, noTake);
}
}
return dp[0][capacity];
}
int main() {
vector<int> val = {1, 30}, wt = {1, 50};
int capacity = 100;
cout << knapSack(capacity, val, wt);
}
class GfG {
static int knapSack(int capacity, int[] val, int[] wt) {
// 2D matrix for tabulation.
int[][] dp = new int[val.length + 1][capacity + 1];
// Calculate maximum profit for each
// item index and knapsack weight.
for (int i = val.length - 1; i >= 0; i--) {
for (int j = 1; j <= capacity; j++) {
int take = 0;
if (j - wt[i] >= 0) {
take = val[i] + dp[i][j - wt[i]];
}
int noTake = dp[i + 1][j];
dp[i][j] = Math.max(take, noTake);
}
}
return dp[0][capacity];
}
public static void main(String[] args) {
int[] val = { 1, 30 };
int[] wt = { 1, 50 };
int capacity = 100;
System.out.println(knapSack(capacity, val, wt));
}
}
def knapSack(capacity, val, wt):
# 2D matrix for tabulation.
dp = [[0 for _ in range(capacity + 1)] for _ in range(len(val) + 1)]
# Calculate maximum profit for each
# item index and knapsack weight.
for i in range(len(val) - 1, -1, -1):
for j in range(1, capacity + 1):
take = 0
if j - wt[i] >= 0:
take = val[i] + dp[i][j - wt[i]]
noTake = dp[i + 1][j]
dp[i][j] = max(take, noTake)
return dp[0][capacity]
if __name__ == "__main__":
val = [1, 30]
wt = [1, 50]
capacity = 100
print(knapSack(capacity, val, wt))
using System;
class GfG {
static int knapSack(int capacity, int[] val, int[] wt) {
// 2D matrix for tabulation.
int[,] dp = new int[val.Length + 1, capacity + 1];
// Calculate maximum profit for each
// item index and knapsack weight.
for (int i = val.Length - 1; i >= 0; i--) {
for (int j = 1; j <= capacity; j++) {
int take = 0;
if (j - wt[i] >= 0) {
take = val[i] + dp[i, j - wt[i]];
}
int noTake = dp[i + 1, j];
dp[i, j] = Math.Max(take, noTake);
}
}
return dp[0, capacity];
}
static void Main() {
int[] val = {1, 30};
int[] wt = {1, 50};
int capacity = 100;
Console.WriteLine(knapSack(capacity, val, wt));
}
}
function knapSack(capacity, val, wt) {
// 2D matrix for tabulation.
let dp = Array.from({ length: val.length + 1 }, () => Array(capacity + 1).fill(0));
// Calculate maximum profit for each
// item index and knapsack weight.
for (let i = val.length - 1; i >= 0; i--) {
for (let j = 1; j <= capacity; j++) {
let take = 0;
if (j - wt[i] >= 0) {
take = val[i] + dp[i][j - wt[i]];
}
let noTake = dp[i + 1][j];
dp[i][j] = Math.max(take, noTake);
}
}
return dp[0][capacity];
}
const val = [1, 30];
const wt = [1, 50];
const capacity = 100;
console.log(knapSack(capacity, val, wt));
Output
100
Using Space Optimized DP – O(n*capacity) Time and O(capacity) Space
This approach further optimizes the tabulation method by reducing the space complexity. Since the current DP state depends only on previously computed capacities, a 1D array is sufficient. The DP array is updated for each capacity by considering inclusion of the current item multiple times where dp[j] represents the maximum profit achievable with a knapsack capacity of j.
#include <iostream>
#include<vector>
using namespace std;
int knapSack(int capacity, vector<int> &val, vector<int> &wt) {
// 1D matrix for tabulation.
vector<int> dp(capacity + 1, 0);
// Calculate maximum profit for each
// item index and knapsack weight.
for (int i = val.size() - 1; i >= 0; i--) {
for (int j = 1; j <= capacity; j++) {
int take = 0;
if (j - wt[i] >= 0) {
take = val[i] + dp[j - wt[i]];
}
int noTake = dp[j];
dp[j] = max(take, noTake);
}
}
return dp[capacity];
}
int main() {
vector<int> val = {1, 30}, wt = {1, 50};
int capacity = 100;
cout << knapSack(capacity, val, wt);
}
class GfG {
static int knapSack(int capacity, int[] val, int[] wt) {
// 1D matrix for tabulation.
int[] dp = new int[capacity + 1];
// Calculate maximum profit for each
// item index and knapsack weight.
for (int i = val.length - 1; i >= 0; i--) {
for (int j = 1; j <= capacity; j++) {
int take = 0;
if (j - wt[i] >= 0) {
take = val[i] + dp[j - wt[i]];
}
int noTake = dp[j];
dp[j] = Math.max(take, noTake);
}
}
return dp[capacity];
}
public static void main(String[] args) {
int[] val = { 1, 30 };
int[] wt = { 1, 50 };
int capacity = 100;
System.out.println(knapSack(capacity, val, wt));
}
}
def knapSack(capacity, val, wt):
# 1D matrix for tabulation.
dp = [0] * (capacity + 1)
# Calculate maximum profit for each
# item index and knapsack weight.
for i in range(len(val) - 1, -1, -1):
for j in range(1, capacity + 1):
take = 0
if j - wt[i] >= 0:
take = val[i] + dp[j - wt[i]]
noTake = dp[j]
dp[j] = max(take, noTake)
return dp[capacity]
if __name__ == "__main__":
val = [1, 30]
wt = [1, 50]
capacity = 100
print(knapSack(capacity, val, wt))
using System;
class GfG {
static int knapSack(int capacity, int[] val, int[] wt) {
// 1D matrix for tabulation.
int[] dp = new int[capacity + 1];
// Calculate maximum profit for each
// item index and knapsack weight.
for (int i = val.Length - 1; i >= 0; i--) {
for (int j = 1; j <= capacity; j++) {
int take = 0;
if (j - wt[i] >= 0) {
take = val[i] + dp[j - wt[i]];
}
int noTake = dp[j];
dp[j] = Math.Max(take, noTake);
}
}
return dp[capacity];
}
static void Main() {
int[] val = { 1, 30 };
int[] wt = { 1, 50};
int capacity = 100;
Console.WriteLine(knapSack(capacity, val, wt));
}
}
function knapSack(capacity, val, wt) {
// 1D matrix for tabulation.
let dp = Array(capacity + 1).fill(0);
// Calculate maximum profit for each
// item index and knapsack weight.
for (let i = val.length - 1; i >= 0; i--) {
for (let j = 1; j <= capacity; j++) {
let take = 0;
if (j - wt[i] >= 0) {
take = val[i] + dp[j - wt[i]];
}
let noTake = dp[j];
dp[j] = Math.max(take, noTake);
}
}
return dp[capacity];
}
const val = [1, 30];
const wt = [1, 50];
const capacity = 100;
console.log(knapSack(capacity, val, wt));
Output
100