Given two integer arrays, val and wt, each of size N, representing the values and weights of N items respectively, and an integer W, representing the maximum capacity of a knapsack, determine the maximum value achievable by selecting a subset of the items such that the total weight of the selected items does not exceed the knapsack capacity W. The goal is to maximize the sum of the values of the selected items while keeping the total weight within the knapsack's capacity.
An infinite supply of each item can be assumed.
Input: val = [5, 11, 13], wt = [2, 4, 6], W = 10
Output: 27
Explanation: Select 2 items with weights 4 and 1 item with weight 2 for a total value of 11+11+5 = 27.
Input: val = [10, 40, 50, 70], wt = [1, 3, 4, 5], W = 8
Output: 110
Explanation: Select items with weights 3 and 5 for a total value of 40 + 70 = 110.
Input: val = [60, 100, 120], wt = [10, 20, 30], W = 60
The first approach that comes to our mind is greedy. A greedy solution will fail in this problem because there is no ‘uniformity’ in data. While selecting a local better choice we may choose an item that will in long term give less value.
As the greedy approach doesn’t work, we will try to generate all possible combinations using recursion and select the combination which gives us the maximum value in the given constraints.
So, initially we need to find f(n-1, W) where W is the overall capacity given to us. f(n-1, W) gives the maximize the sum of the values of the selected items from index 0 to n-1 with capacity W of the knapsack.
Exclude the current element from the knapsack: First try to find a solution without considering the current index item. If we exclude the current item, the capacity of the bag will not be affected and the value added will be 0 for the current item. So call the recursive function f(ind-1,W).
Include the current element in the knapsack: Try to find a solution by considering the current item to the knapsack. As we have included the item, the capacity of the knapsack will be updated to W-wt[ind] and the current item’s value (val[ind]) will also be added to the further recursive call answer.
Now here is the catch, as there is an unlimited supply of coins, we want to again form a solution with the same item value. So we will not recursively call for f(ind-1, W-wt[ind]) rather stay at that index only and call for f(ind, W-wt[ind]) to find the answer.
Note: Consider the current item in the knapsack only when the current element’s weight is less than or equal to the capacity ‘W’ of the knapsack, if it isn’t do not consider it.
f(ind, W){
notTake = 0 + f(ind-1, W)
take = INT_MIN
if(wt[ind] <= W)
take = val + f(ind, W-wt[ind])
}
f(ind, W){
notTake = 0 + f(ind-1, W)
take = INT_MIN
if(wt[ind] <= W)
take = val + f(ind, W-wt[ind])
return max(notTake, take)
}
#include <bits/stdc++.h>
using namespace std;
class Solution{
private:
// Function to solve the unbounded knapsack problem
int func(vector<int>& wt, vector<int>& val, int ind, int W) {
// Base case: if we're at the first item
if (ind == 0) {
/* Calculate and return the maximum
value for the given weight limit*/
return (W / wt[0]) * val[0];
}
/* Calculate the maximum value
without taking the current item*/
int notTaken = 0 + func(wt, val, ind - 1, W);
/* Calculate the maximum value
by taking the current item*/
int taken = INT_MIN;
if (wt[ind] <= W)
taken = val[ind] + func(wt, val, ind, W - wt[ind]);
// Return the maximum value
return max(notTaken, taken);
}
public:
// Function to solve the unbounded knapsack problem
int unboundedKnapsack(vector<int>& wt, vector<int>& val, int n, int W) {
/* Call the utility function to
calculate the maximum value*/
return func(wt, val, n - 1, W);
}
};
int main() {
vector<int> wt = {2, 4, 6};
vector<int> val = {5, 11, 13};
int W = 10;
int n = wt.size();
// Create an instance of Solution class
Solution sol;
cout << "The Maximum value of items the thief can steal is " << sol.unboundedKnapsack(wt, val, n, W) << endl;
return 0;
}
import java.util.*;
class Solution {
// Function to solve the unbounded knapsack problem
private int func(int[] wt, int[] val, int ind, int W) {
// Base case: if we're at the first item
if (ind == 0) {
/* Calculate and return the maximum
value for the given weight limit*/
return (W / wt[0]) * val[0];
}
/* Calculate the maximum value
without taking the current item*/
int notTaken = func(wt, val, ind - 1, W);
/* Calculate the maximum value
by taking the current item*/
int taken = Integer.MIN_VALUE;
if (wt[ind] <= W)
taken = val[ind] + func(wt, val, ind, W - wt[ind]);
// Return the maximum value
return Math.max(notTaken, taken);
}
// Function to solve the unbounded knapsack problem
public int unboundedKnapsack(int[] wt, int[] val, int n, int W) {
/* Call the utility function
to calculate the maximum value*/
return func(wt, val, n - 1, W);
}
public static void main(String[] args) {
int[] wt = {2, 4, 6};
int[] val = {5, 11, 13};
int W = 10;
int n = wt.length;
// Create an instance of Solution class
Solution sol = new Solution();
System.out.println("The Maximum value of items the thief can steal is " + sol.unboundedKnapsack(wt, val, n, W));
}
}
class Solution:
#Function to solve the unbounded knapsack problem
def func(self, wt, val, ind, W):
# Base case: if we're at the first item
if ind == 0:
""" Calculate and return the maximum
value for the given weight limit"""
return (W // wt[0]) * val[0]
""" Calculate the maximum value
without taking the current item"""
not_taken = self.func(wt, val, ind - 1, W)
""" Calculate the maximum value
by taking the current item"""
taken = float('-inf')
if wt[ind] <= W:
taken = val[ind] + self.func(wt, val, ind, W - wt[ind])
""" Store the maximum value in
the DP table and return it"""
return max(not_taken, taken)
# Function to solve the unbounded knapsack problem
def unboundedKnapsack(self, wt, val, n, W):
""" Call the utility function
to calculate the maximum value"""
return self.func(wt, val, n - 1, W)
if __name__ == "__main__":
wt = [2, 4, 6]
val = [5, 11, 13]
W = 10
n = len(wt)
# Create an instance of Solution class
sol = Solution()
print("The Maximum value of items the thief can steal is", sol.unboundedKnapsack(wt, val, n, W))
class Solution {
// Function to solve the unbounded knapsack problem
func(wt, val, ind, W) {
// Base case: if we're at the first item
if (ind === 0) {
/* Calculate and return the maximum
value for the given weight limit*/
return Math.floor(W / wt[0]) * val[0];
}
/* Calculate the maximum value
without taking the current item*/
const notTaken = this.func(wt, val, ind - 1, W);
/* Calculate the maximum value
by taking the current item*/
let taken = -Infinity;
if (wt[ind] <= W) {
taken = val[ind] + this.func(wt, val, ind, W - wt[ind]);
}
// Return the maximum value
return Math.max(notTaken, taken);
}
// Function to solve the unbounded knapsack problem
unboundedKnapsack(wt, val, n, W) {
/* Call the utility function to
calculate the maximum value*/
return this.func(wt, val, n - 1, W);
}
}
const wt = [2, 4, 6];
const val = [5, 11, 13];
const W = 10;
const n = wt.length;
// Create an instance of Solution class
const sol = new Solution();
console.log("The Maximum value of items the thief can steal is " + sol.unboundedKnapsack(wt, val, n, W));
If we draw the recursion tree, we will see that there are overlapping subproblems. Hence the DP approaches can be applied to the recursive solution.
The dp array stores the calculations of subproblems. Initialize dp array with -1, to indicate that no subproblem has been solved yet.
#include <bits/stdc++.h>
using namespace std;
class Solution{
private:
// Function to solve the unbounded knapsack problem
int func(vector<int>& wt, vector<int>& val, int ind, int W, vector<vector<int>>& dp) {
// Base case: if we're at the first item
if (ind == 0) {
/* Calculate and return the maximum
value for the given weight limit*/
return (W / wt[0]) * val[0];
}
/* If the result for this index and weight
limit is already calculated, return it*/
if (dp[ind][W] != -1)
return dp[ind][W];
/* Calculate the maximum value
without taking the current item*/
int notTaken = 0 + func(wt, val, ind - 1, W, dp);
/* Calculate the maximum value
by taking the current item*/
int taken = INT_MIN;
if (wt[ind] <= W)
taken = val[ind] + func(wt, val, ind, W - wt[ind], dp);
/* Store the maximum value in
the DP table and return it*/
return dp[ind][W] = max(notTaken, taken);
}
public:
// Function to solve the unbounded knapsack problem
int unboundedKnapsack(vector<int>& wt, vector<int>& val, int n, int W) {
vector<vector<int>> dp(n, vector<int>(W + 1, -1));
/* Call the utility function to
calculate the maximum value*/
return func(wt, val, n - 1, W, dp);
}
};
int main() {
vector<int> wt = {2, 4, 6};
vector<int> val = {5, 11, 13};
int W = 10;
int n = wt.size();
// Create an instance of Solution class
Solution sol;
cout << "The Maximum value of items the thief can steal is " << sol.unboundedKnapsack(wt, val, n, W) << endl;
return 0;
}
import java.util.*;
class Solution {
// Function to solve the unbounded knapsack problem
private int func(int[] wt, int[] val, int ind, int W, int[][] dp) {
// Base case: if we're at the first item
if (ind == 0) {
/* Calculate and return the maximum
value for the given weight limit*/
return (W / wt[0]) * val[0];
}
/* If the result for this index and weight
limit is already calculated, return it*/
if (dp[ind][W] != -1)
return dp[ind][W];
/* Calculate the maximum value
without taking the current item*/
int notTaken = func(wt, val, ind - 1, W, dp);
/* Calculate the maximum value
by taking the current item*/
int taken = Integer.MIN_VALUE;
if (wt[ind] <= W)
taken = val[ind] + func(wt, val, ind, W - wt[ind], dp);
/* Store the maximum value in
the DP table and return it*/
return dp[ind][W] = Math.max(notTaken, taken);
}
// Function to solve the unbounded knapsack problem
public int unboundedKnapsack(int[] wt, int[] val, int n, int W) {
int[][] dp = new int[n][W + 1];
for (int[] row : dp) {
Arrays.fill(row, -1);
}
/* Call the utility function
to calculate the maximum value*/
return func(wt, val, n - 1, W, dp);
}
public static void main(String[] args) {
int[] wt = {2, 4, 6};
int[] val = {5, 11, 13};
int W = 10;
int n = wt.length;
// Create an instance of Solution class
Solution sol = new Solution();
System.out.println("The Maximum value of items the thief can steal is " + sol.unboundedKnapsack(wt, val, n, W));
}
}
class Solution:
#Function to solve the unbounded knapsack problem
def func(self, wt, val, ind, W, dp):
# Base case: if we're at the first item
if ind == 0:
""" Calculate and return the maximum
value for the given weight limit"""
return (W // wt[0]) * val[0]
""" If the result for this index and weight
limit is already calculated, return it"""
if dp[ind][W] != -1:
return dp[ind][W]
""" Calculate the maximum value
without taking the current item"""
not_taken = self.func(wt, val, ind - 1, W, dp)
""" Calculate the maximum value
by taking the current item"""
taken = float('-inf')
if wt[ind] <= W:
taken = val[ind] + self.func(wt, val, ind, W - wt[ind], dp)
""" Store the maximum value in
the DP table and return it"""
dp[ind][W] = max(not_taken, taken)
return dp[ind][W]
# Function to solve the unbounded knapsack problem
def unboundedKnapsack(self, wt, val, n, W):
dp = [[-1] * (W + 1) for _ in range(n)]
""" Call the utility function
to calculate the maximum value"""
return self.func(wt, val, n - 1, W, dp)
if __name__ == "__main__":
wt = [2, 4, 6]
val = [5, 11, 13]
W = 10
n = len(wt)
# Create an instance of Solution class
sol = Solution()
print("The Maximum value of items the thief can steal is", sol.unboundedKnapsack(wt, val, n, W))
class Solution {
// Function to solve the unbounded knapsack problem
func(wt, val, ind, W, dp) {
// Base case: if we're at the first item
if (ind === 0) {
/* Calculate and return the maximum
value for the given weight limit*/
return Math.floor(W / wt[0]) * val[0];
}
/* If the result for this index and weight
limit is already calculated, return it*/
if (dp[ind][W] !== -1) {
return dp[ind][W];
}
/* Calculate the maximum value
without taking the current item*/
const notTaken = this.func(wt, val, ind - 1, W, dp);
/* Calculate the maximum value
by taking the current item*/
let taken = -Infinity;
if (wt[ind] <= W) {
taken = val[ind] + this.func(wt, val, ind, W - wt[ind], dp);
}
/* Store the maximum value in
the DP table and return it*/
dp[ind][W] = Math.max(notTaken, taken);
return dp[ind][W];
}
// Function to solve the unbounded knapsack problem
unboundedKnapsack(wt, val, n, W) {
const dp = Array.from({ length: n }, () => Array(W + 1).fill(-1));
/* Call the utility function to
calculate the maximum value*/
return this.func(wt, val, n - 1, W, dp);
}
}
const wt = [2, 4, 6];
const val = [5, 11, 13];
const W = 10;
const n = wt.length;
// Create an instance of Solution class
const sol = new Solution();
console.log("The Maximum value of items the thief can steal is " + sol.unboundedKnapsack(wt, val, n, W));
In order to convert a recursive code to tabulation code, we try to convert the recursive code to tabulation and here are the steps:
#include <bits/stdc++.h>
using namespace std;
class Solution{
public:
// Function to solve the unbounded knapsack problem
int unboundedKnapsack(vector<int>& wt, vector<int>& val, int n, int W) {
vector<vector<int>> dp(n, vector<int>(W + 1, 0));
// Base Condition
for (int i = wt[0]; i <= W; i++) {
// Calculate maximum value for the first item
dp[0][i] = (i / wt[0]) * val[0];
}
for (int ind = 1; ind < n; ind++) {
for (int cap = 0; cap <= W; cap++) {
// Maximum value without taking current item
int notTaken = 0 + dp[ind - 1][cap];
int taken = INT_MIN;
if (wt[ind] <= cap)
// Maximum value by taking current item
taken = val[ind] + dp[ind][cap - wt[ind]];
// Store the maximum value in the DP table
dp[ind][cap] = max(notTaken, taken);
}
}
/* Return the maximum value considering
all items and the knapsack capacity*/
return dp[n - 1][W];
}
};
int main() {
vector<int> wt = {2, 4, 6};
vector<int> val = {5, 11, 13};
int W = 10;
int n = wt.size();
//Create an instance of Solution class
Solution sol;
// Print the result
cout << "The Maximum value of items the thief can steal is " << sol.unboundedKnapsack(wt, val, n, W) << endl;
return 0;
}
import java.util.*;
class Solution {
// Function to solve the unbounded knapsack problem
public int unboundedKnapsack(int[] wt, int[] val, int n, int W) {
int[][] dp = new int[n][W + 1];
// Base Condition
for (int i = wt[0]; i <= W; i++) {
// Calculate maximum value for the first item
dp[0][i] = (i / wt[0]) * val[0];
}
for (int ind = 1; ind < n; ind++) {
for (int cap = 0; cap <= W; cap++) {
// Maximum value without taking current item
int notTaken = dp[ind - 1][cap];
int taken = Integer.MIN_VALUE;
if (wt[ind] <= cap)
// Maximum value by taking current item
taken = val[ind] + dp[ind][cap - wt[ind]];
// Store the maximum value in the DP table
dp[ind][cap] = Math.max(notTaken, taken);
}
}
/* Return the maximum value considering
all items and the knapsack capacity*/
return dp[n - 1][W];
}
public static void main(String[] args) {
int[] wt = {2, 4, 6};
int[] val = {5, 11, 13};
int W = 10;
int n = wt.length;
// Create an instance of Solution class
Solution sol = new Solution();
// Print the result
System.out.println("The Maximum value of items the thief can steal is " + sol.unboundedKnapsack(wt, val, n, W));
}
}
class Solution:
# Function to solve the unbounded knapsack problem
def unboundedKnapsack(self, wt, val, n, W):
dp = [[0] * (W + 1) for _ in range(n)]
# Base Condition
for i in range(wt[0], W + 1):
# Calculate maximum value for the first item
dp[0][i] = (i // wt[0]) * val[0]
for ind in range(1, n):
for cap in range(W + 1):
# Maximum value without taking current item
not_taken = dp[ind - 1][cap]
taken = float('-inf')
if wt[ind] <= cap:
# Maximum value by taking current item
taken = val[ind] + dp[ind][cap - wt[ind]]
# Store the maximum value in the DP table
dp[ind][cap] = max(not_taken, taken)
""" Return the maximum value considering
all items and the knapsack capacity"""
return dp[n - 1][W]
if __name__ == "__main__":
wt = [2, 4, 6]
val = [5, 11, 13]
W = 10
n = len(wt)
# Create an instance of Solution class
sol = Solution()
# Print the result
print("The Maximum value of items the thief can steal is", sol.unboundedKnapsack(wt, val, n, W))
class Solution {
// Function to solve the unbounded knapsack problem
unboundedKnapsack(wt, val, n, W) {
const dp = Array.from({ length: n }, () => Array(W + 1).fill(0));
// Base Condition
for (let i = wt[0]; i <= W; i++) {
// Calculate maximum value for the first item
dp[0][i] = Math.floor(i / wt[0]) * val[0];
}
for (let ind = 1; ind < n; ind++) {
for (let cap = 0; cap <= W; cap++) {
// Maximum value without taking current item
const notTaken = dp[ind - 1][cap];
let taken = -Infinity;
if (wt[ind] <= cap) {
// Maximum value by taking current item
taken = val[ind] + dp[ind][cap - wt[ind]];
}
// Store the maximum value in the DP table
dp[ind][cap] = Math.max(notTaken, taken);
}
}
/* Return the maximum value considering
all items and the knapsack capacity*/
return dp[n - 1][W];
}
}
const wt = [2, 4, 6];
const val = [5, 11, 13];
const W = 10;
const n = wt.length;
// Create an instance of Solution class
const sol = new Solution();
// Print the result
console.log("The Maximum value of items the thief can steal is " + sol.unboundedKnapsack(wt, val, n, W));
If we observe the relation obtained in the tabulation approach, dp[ind][cap] = max(dp[ind-1][cap] ,dp[ind][cap-wt[ind]]. We find that to calculate a value of a cell of the dp array, only the previous row values (say prev) are needed. So, we don’t need to store an entire array. Hence we can space optimize it. We will be space optimizing this solution using only one row.
In the relation, dp[ind-1][cap] and dp[ind-1][cap - wt[ind]], if we are at a column, only the same column value from previous row is required and other values will be from the current row itself. So we do not need to store an entire array for it.
We can use the 'cur' row itself to store the required value in the following way:
#include <bits/stdc++.h>
using namespace std;
class Solution{
public:
// Function to solve the unbounded knapsack problem
int unboundedKnapsack(vector<int>& wt, vector<int>& val, int n, int W) {
// Create a vector to store current DP state
vector<int> cur(W + 1, 0);
// Base Condition
for (int i = wt[0]; i <= W; i++) {
// Calculate the maximum value for first item
cur[i] = (i / wt[0]) * val[0];
}
for (int ind = 1; ind < n; ind++) {
for (int cap = 0; cap <= W; cap++) {
// Maximum value without taking current item
int notTaken = cur[cap];
int taken = INT_MIN;
if (wt[ind] <= cap)
// Maximum value by taking current item
taken = val[ind] + cur[cap - wt[ind]];
/* Store the maximum value
in the current DP state*/
cur[cap] = max(notTaken, taken);
}
}
/* Return the maximum value considering
all items and the knapsack capacity*/
return cur[W];
}
};
int main() {
vector<int> wt = {2, 4, 6};
vector<int> val = {5, 11, 13};
int W = 10;
int n = wt.size();
//Create an instance of Solution class
Solution sol;
// Print the result
cout << "The Maximum value of items the thief can steal is " << sol.unboundedKnapsack(wt, val, n, W) << endl;
return 0;
}
import java.util.*;
class Solution {
// Function to solve the unbounded knapsack problem
public int unboundedKnapsack(int[] wt, int[] val, int n, int W) {
// Create a vector to store current DP state
int[] cur = new int[W + 1];
// Base Condition
for (int i = wt[0]; i <= W; i++) {
// Calculate the maximum value for first item
cur[i] = (i / wt[0]) * val[0];
}
for (int ind = 1; ind < n; ind++) {
for (int cap = 0; cap <= W; cap++) {
// Maximum value without taking current item
int notTaken = cur[cap];
int taken = Integer.MIN_VALUE;
if (wt[ind] <= cap)
// Maximum value by taking current item
taken = val[ind] + cur[cap - wt[ind]];
/* Store the maximum value
in the current DP state*/
cur[cap] = Math.max(notTaken, taken);
}
}
/* Return the maximum value considering
all items and the knapsack capacity*/
return cur[W];
}
}
public class Main {
public static void main(String[] args) {
int[] wt = {2, 4, 6};
int[] val = {5, 11, 13};
int W = 10;
int n = wt.length;
// Create an instance of Solution class
Solution sol = new Solution();
// Print the result
System.out.println("The Maximum value of items the thief can steal is " + sol.unboundedKnapsack(wt, val, n, W));
}
}
class Solution:
# Function to solve the unbounded knapsack problem
def unboundedKnapsack(self, wt, val, n, W):
# Create a list to store current DP state
cur = [0] * (W + 1)
# Base Condition
for i in range(wt[0], W + 1):
# Calculate the maximum value for first item
cur[i] = (i // wt[0]) * val[0]
for ind in range(1, n):
for cap in range(W + 1):
# Maximum value without taking current item
notTaken = cur[cap]
taken = float('-inf')
if wt[ind] <= cap:
# Maximum value by taking current item
taken = val[ind] + cur[cap - wt[ind]]
# Store the maximum value in the current DP state
cur[cap] = max(notTaken, taken)
""" Return the maximum value considering
all items and the knapsack capacity"""
return cur[W]
if __name__ == "__main__":
wt = [2, 4, 6]
val = [5, 11, 13]
W = 10
n = len(wt)
# Create an instance of Solution class
sol = Solution()
# Print the result
print("The Maximum value of items the thief can steal is", sol.unboundedKnapsack(wt, val, n, W))
class Solution {
// Function to solve the unbounded knapsack problem
unboundedKnapsack(wt, val, n, W) {
// Create an array to store current DP state
let cur = new Array(W + 1).fill(0);
// Base Condition
for (let i = wt[0]; i <= W; i++) {
// Calculate the maximum value for first item
cur[i] = Math.floor(i / wt[0]) * val[0];
}
for (let ind = 1; ind < n; ind++) {
for (let cap = 0; cap <= W; cap++) {
// Maximum value without taking current item
let notTaken = cur[cap];
let taken = Number.MIN_SAFE_INTEGER;
if (wt[ind] <= cap) {
// Maximum value by taking current item
taken = val[ind] + cur[cap - wt[ind]];
}
// Store the maximum value in the current DP state
cur[cap] = Math.max(notTaken, taken);
}
}
/* Return the maximum value considering
all items and the knapsack capacity*/
return cur[W];
}
}
const wt = [2, 4, 6];
const val = [5, 11, 13];
const W = 10;
const n = wt.length;
// Create an instance of Solution class
const sol = new Solution();
// Print the result
console.log("The Maximum value of items the thief can steal is " + sol.unboundedKnapsack(wt, val, n, W));
Q: Why do we iterate over items first instead of W?
A: Iterating items first ensures that each item can be used multiple times, allowing optimal selection.
Q: Why do we update dp[w] from left to right instead of right to left?
A: Since we can pick unlimited items, updating from left to right ensures each item can be used multiple times.
Q: What if we had a limit on the number of times each item can be picked (Bounded Knapsack)?
A: Use a 3D DP table (dp[i][w][c]), where c tracks the count of times an item is used.
Q: How can this problem be solved using recursion with memoization?
A: Define knapsack(i,w)=max(knapsack(i,w−wt[i])+val[i],knapsack(i−1,w)) Use top-down memoization (O(N × W) complexity).