Given N items with corresponding weights (w1, w2, ... wn) and values (v1, v2, ... vn), along with the total capacity weight W for a knapsack, determine the maximum value that can be achieved by filling the knapsack. Here, each item can be used any number of times, making it an unbounded knapsack problem. The goal is to maximise the total value of items selected such that their total weight does not exceed W.
Input:
Weights =[2,4,6], Values =[8,16,24], Capacity =10Output:
Maximum value =40Explanation:
- Item 1 can be taken 5times(2×5=10 weight)→ Total value =40
Input:
Weights =[1,3,4], Values =[10,30,50], Capacity =7Output:
Maximum value =90Explanation:
- Item 1is taken once(1 weight,10 value).- Item 2is taken twice(3×2=6 weight,60 value)→ Total value =90
Additionally, there are special cases of the knapsack problem:
All items have equal weight: In this scenario, the goal is to maximise profit as all items weigh the same. Sorting items by value and then picking the highest-value items will solve this variant using a greedy approach.
All items have equal value: If all items have the same value, selecting as many items as possible within the weight limit becomes the target. Sorting items by size and selecting the smallest ones until the knapsack is full provides an efficient solution.
Another prominent instance of the knapsack problem is the Subset Sum Problem, where each item’s value is proportional to its size. For example, consider a collection of gold weights: heavier weights hold more value, and the goal is to fill the knapsack while minimising leftover space. Additionally, this problem appears in cases where you aim to achieve a specific total (C) as closely as possible.
However, this subset sum problem cannot be solved using a greedy algorithm because it is NP-complete. This means it belongs to a class of problems for which no known polynomial-time solutions exist unless P = NP is proven. It is often linked to the Subset Sum Problem and related algorithms like the Integer Partition Algorithm.
#include<iostream>#include<algorithm>usingnamespace std;
classSolution {
public:int knapsack(int capacity, int weights[], int values[], int itemCount) {
// Base Case: No items left or capacity is zero
if (itemCount ==0|| capacity ==0)
return0;
// If current item's weight exceeds the remaining capacity
if (weights[itemCount -1] > capacity)
return knapsack(capacity, weights, values, itemCount -1);
// Calculate the maximum value by including or excluding the current item
else {
int includeItem = values[itemCount -1] + knapsack(capacity - weights[itemCount -1], weights, values, itemCount);
int excludeItem = knapsack(capacity, weights, values, itemCount -1);
returnmax(includeItem, excludeItem);
}
}
};
// Example usage
intmain() {
Solution solution;
int weights[] = {2, 4, 6};
int values[] = {8, 16, 24};
int capacity =10;
int itemCount =sizeof(weights) /sizeof(weights[0]);
cout <<"Maximum value: "<< solution.knapsack(capacity, weights, values, itemCount) << endl;
return0;
}
classSolution:
defknapSack(self, W, weights, values, n):
# Base Case: No items left or capacity is zeroif n ==0or W ==0:
return0# If current item's weight exceeds the remaining capacityif weights[n -1] > W:
return self.knapSack(W, weights, values, n -1)
# Otherwise, calculate the maximum value by including or excluding the current itemelse:
include_current = values[n -1] + self.knapSack(W - weights[n -1], weights, values, n)
exclude_current = self.knapSack(W, weights, values, n -1)
return max(include_current, exclude_current)
# Example usage:solution = Solution()
weights = [2, 4, 6]
values = [8, 16, 24]
capacity =10print("Maximum value:", solution.knapSack(capacity, weights, values, len(weights)))
Memoization adds a cache to store previously computed results of subproblems. This avoids redundant computations, making the recursive solution more efficient.
#include<iostream>#include<vector>usingnamespace std;
classSolution {
public:// Memoized knapsack helper
int knapsackMemo(int capacity, int weights[], int values[], int itemCount, vector<vector<int>>& memo) {
// Base Case: No items left or capacity is zero
if (itemCount ==0|| capacity ==0)
return0;
// If result for this subproblem is already computed, return it
if (memo[itemCount][capacity] !=-1)
return memo[itemCount][capacity];
// If current item's weight exceeds the remaining capacity
if (weights[itemCount -1] > capacity) {
memo[itemCount][capacity] = knapsackMemo(capacity, weights, values, itemCount -1, memo);
} else {
// Calculate the maximum value by including or excluding the current item
int includeItem = values[itemCount -1] + knapsackMemo(capacity - weights[itemCount -1], weights, values, itemCount, memo);
int excludeItem = knapsackMemo(capacity, weights, values, itemCount -1, memo);
memo[itemCount][capacity] = max(includeItem, excludeItem);
}
return memo[itemCount][capacity];
}
intknapsack(int capacity, int weights[], int values[], int itemCount) {
// Initialize memo table with -1
vector<vector<int>> memo(itemCount +1, vector<int>(capacity +1, -1));
// Call the helper function
return knapsackMemo(capacity, weights, values, itemCount, memo);
}
};
// Example usage
intmain() {
Solution solution;
int weights[] = {2, 4, 6};
int values[] = {8, 16, 24};
int capacity =10;
int itemCount =sizeof(weights) /sizeof(weights[0]);
cout <<"Maximum value: "<< solution.knapsack(capacity, weights, values, itemCount) << endl;
return0;
}
Now, lets solve it using bottom-up dynamic programming approach. The relationship between the subproblems can be expressed as:
1
value(w) = max {value(w - wi) + vi} for all i such that wi ≤ w
This means that to compute the maximum value for capacity w, we consider taking the current item of weight wi multiple times (if allowed) and add its value vi to the maximum value of the remaining capacity (w - wi). We calculate the optimal value by trying this for all items.
KnapsackUnbounded(W, weights, values, n)
Initializevalue[0...W] ←0// DP array to store max values for each capacityforwfrom1toW:
maxValue←0forifrom1ton:
ifweights[i] ≤w:
maxValue = max(maxValue, value[w-weights[i]] +values[i])
value[w] ←maxValuereturnvalue[W] // Maximum value for full capacity
Base Case: The maximum value for a capacity of 0 (value[0]) is 0 since we cannot include any items.
Iterative Calculation: For each capacity w, we iterate through all items and compute the maximum value by including the current item multiple times (if possible) and comparing values from different combinations.
Final Value: The result (value[W]) gives the maximum value achievable for the given knapsack capacity W.