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)
Updated on: 2025-04-29T15:56:46+05:30

16K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements