
Data Structure
Networking
RDBMS
Operating System
Java
MS Excel
iOS
HTML
CSS
Android
Python
C Programming
C++
C#
MongoDB
MySQL
Javascript
PHP
- Selected Reading
- UPSC IAS Exams Notes
- Developer's Best Practices
- Questions and Answers
- Effective Resume Writing
- HR Interview Questions
- Computer Glossary
- Who is Who
Solve Knapsack Problem Using Dynamic Programming in C++
In the 0-1 knapsack problem, a set of items is given, each with a weight and a value. We need to determine the number of each item to include in a collection so that the total weight is less than or equal to the given limit and the total value is as large as possible.
In this article, we will discuss how to solve the 0-1 knapsack problem using dynamic programming. Dynamic Programming is a technique where we solve problems by breaking them into smaller sub-problems. If a subproblem is solved multiple times, DP stores the results so that we don't have to compute them again.
We have a weight array, their values array, and a given maximum weight capacity. Our task is to find the maximum value using the given weight array with a given weight constraint.
Example
Here is an example of 0-1 knapsack problem:
Input: Weights: 1 2 3 6 7 4 Values: 10 20 25 40 60 70 Max Weight Capacity: 7 Output: Maximum value: 100
Here is a list of different techniques to solve the 0-1 knapsack problem using dynamic programming in C++ with step-wise explanation and complete example codes.
Using Memoization With Recursion
The following approach uses memoization with recursion. We have used a 2D vector dp to avoid the redundant calculation. It stores the results in this vector and the results are used again when needed.
- We have created a function ksMemo() that accepts the weight, value, maximum capacity, number of elements, and a 2D DP table as arguments.
- In this function, first, we check if the result is already present in this 2D vector or not. If the result is already present, then we use the value and if the result is not present, then we calculate the result and store it for future use.
- Then we have defined three cases, i.e. the base case, when an item is included, and when the item is excluded. The base case returns 0 when there is no item left or capacity becomes 0.
- If the weight is less than or equal to the maximum capacity then we include its value and recursively call the ksMemo() function with the remaining elements by reducing the capacity.
- Similarly, if the value is excluded, then we recursively call the ksMemo() function without reducing the capacity.
- We store the maximum value out of the include and exclude in the DP table.
- In the end, the DP table is returned that consists of the maximum value with the maximum weight capacity.
Example
The following example implements the above-mentioned steps to implement the 0-1 knapsack problem using memoization with recursion.
#include <iostream> #include <vector> using namespace std; int ksMemo(int weights[], int values[], int n, int capacity, vector<vector<int>> &dp) { if (n == 0 || capacity == 0) return 0; // Base case if (dp[n][capacity] != -1) return dp[n][capacity]; // Check if result is already computed // Exclude the item int exclude = ksMemo(weights, values, n - 1, capacity, dp); // Include the item (if it fits in the knapsack) int include = 0; if (weights[n - 1] <= capacity) include = values[n - 1] + ksMemo(weights, values, n - 1, capacity - weights[n - 1], dp); dp[n][capacity] = max(include, exclude); // Store the result return dp[n][capacity]; } int main() { int weights[] = {1, 2, 3, 6, 7, 4}; cout << "Weights: "; for (int i = 0; i < 6; i++) cout << weights[i] << " "; int values[] = {10, 20, 25, 40, 60, 70}; cout << "\nValues: "; for (int i = 0; i < 6; i++) cout << values[i] << " "; int capacity = 7; cout << "\nMax Weight Capacity: " << capacity << endl; int n = 6; vector<vector<int>> dp(n + 1, vector<int>(capacity + 1, -1)); cout << "Maximum value: " << ksMemo(weights, values, n, capacity, dp) << endl; return 0; }
The output of the above code is:
Weights: 1 2 3 6 7 4 Values: 10 20 25 40 60 70 Max Weight Capacity: 7 Maximum value: 100
Using Bottom-Up or Tabulation
To solve the 0-1 Knapsack problem, we have used a 2D DP table. In this table, we fill the table based on the earlier computed results iteratively.
- We have defined a ksDpTable() function that accepts weight, values, maximum capacity, and number of items as arguments.
- In this function, first, we have created a DP table of (n+1) rows and (capacity+1) columns. Initially, this table is filled with 0s. It will store the maximum value.
- The outer for loop iterates over all elements n, while the inner loop checks if the weight value is less than the maximum capacity.
- If the item is excluded, then we store the previous value and move on to the next element.
- If the element is included, then we update the current maximum weight in the table. Then the result is returned after the loop ends as dp[n][capacity].
Example
Here is an example of a 0-1 knapsack problem using the tabulation method.
#include <iostream> #include <vector> using namespace std; int ksDpTable(int weights[], int values[], int n, int capacity) { vector<vector<int>> dp(n + 1, vector<int>(capacity + 1, 0)); for (int i = 1; i <= n; ++i) { for (int w = 1; w <= capacity; ++w) { // If the item is not included dp[i][w] = dp[i - 1][w]; // If the item is included if (weights[i - 1] <= w) dp[i][w] = max(dp[i][w], values[i - 1] + dp[i - 1][w - weights[i - 1]]); } } return dp[n][capacity]; } int main() { int weights[] = {1, 2, 3, 6, 7, 4}; cout << "Weights: "; for (int i = 0; i < 6; i++) cout << weights[i] << " "; int values[] = {10, 20, 25, 40, 60, 70}; cout << "\nValues: "; for (int i = 0; i < 6; i++) cout << values[i] << " "; int capacity = 7; cout << "\nMax Weight Capacity: " << capacity << endl; int n = 6; cout << "Maximum value: " << ksDpTable(weights, values, n, capacity) << endl; return 0; }
The output of the above code is:
Weights: 1 2 3 6 7 4 Values: 10 20 25 40 60 70 Max Weight Capacity: 7 Maximum value: 100
Using Space Optimized Approach
In this approach, we will use a single 1D array instead of a 2D table to store the results. Just like the previous approach where we had used a 2D table, here also, we will iteratively fill the array and maintain just the previous value of weight. This approach also follows the same approach as a 2D table, except we will be using a 1D array.
Example
This example demonstrates the use of a single-dimensional array to solve the 0-1 knapsack problem.
#include <iostream> #include <vector> using namespace std; int ksOpt(int weights[], int values[], int n, int capacity) { vector<int> dp(capacity + 1, 0); for (int i = 0; i < n; ++i) { for (int w = capacity; w >= weights[i]; --w) { dp[w] = max(dp[w], values[i] + dp[w - weights[i]]); } } return dp[capacity]; } int main() { int weights[] = {1, 2, 3, 6, 7, 4}; cout << "Weights: "; for (int i = 0; i < 6; i++) cout << weights[i] << " "; int values[] = {10, 20, 25, 40, 60, 70}; cout << "\nValues: "; for (int i = 0; i < 6; i++) cout << values[i] << " "; int capacity = 7; cout << "\nMax Weight Capacity: " << capacity << endl; int n = 6; cout << "Maximum value: " << ksOpt(weights, values, n, capacity) << endl; return 0; }
The output of the above code is:
Weights: 1 2 3 6 7 4 Values: 10 20 25 40 60 70 Max Weight Capacity: 7 Maximum value: 100
Comparison of Approaches
Here is a comparison of the time and space complexity of all the above approaches.
Approach | Time Complexity | Space Complexity |
---|---|---|
Memoization with Recursion | O(n*w) | O(n*w) |
Bottom-Up or Tabulation | O(n*w) | O(n*w) |
Space Optimized Approach | O(n*w) | O(w) |