A Space Optimized DP solution for 0-1 Knapsack Problem

Difficulty Level Medium
Frequently asked in Amazon BlackRock ByteDance CodeNation JP Morgan Netskope Ola Cabs Qualcomm
Dynamic Programming KnapsackViews 2048

Problem Statement

We are given a knapsack which can hold some weight, we need to pick some of the items out of given items with some value. The items should be picked such that the value of the knapsack ( total value of picked up items ) should be maximized. We also need to take care that the weight of picked items does not exceed the value of the knapsack. Here, we can have a large knapsack weight. We cannot divide the items, i.e. we cannot take half part of an item or one-third. Thus, either we take an item or not which gives the problem its name 0-1 Knapsack Problem. Since knapsack can have large weight, find a space-optimized DP solution for 0-1 knapsack problem.

Example

A Space Optimized DP solution for 0-1 Knapsack Problem

Number of Items = 3
Weight of Knapsack = 4
Weight of given items = {4, 5, 1}
Value of given items = {10, 20, 30}
30

Explanation: We choose the last element because that gives us the best result (or maximum value)

 

Number of Items = 5
Weight of Knapsack = 50
Weight of given items = {10, 10, 10, 10, 10}
Value of given items = {1, 2, 3, 4, 5}
15

Explanation: We can simply choose all the items since our knapsack weight equals the total weight of all the items.

 

Approach for a space optimized DP solution for 0-1 Knapsack Problem

Generally, we solve 0-1 Knapsack using dynamic programming. This is one of the standard dynamic programming problems and many other standard dynamic programming problems follow the same pattern.

dp[i][j] = operation(dp[i-1][j], dp[i-1][j-wt[i] + val[i])

Here dp[i-1][j] represents we did not take the current item and

         dp[i-1][j-wt[i]] shows the reduced subproblem.

By operation I mean we generally either maximize a quantity or minimize the quantity.

The only problem which arises with this approach is its quadratic space complexity. We normally make a dp array or dp table of dimensions weight of knapsack x number of items. In cases where we are low on memory. We can further optimize our standard knapsack solution. Taking a look at the recursive formula gives us an insight that any state in the dp matrix depends on the cell just above it or left of it. Then we can just keep two rows of the dp table, the first current row, and second, the row before the current row. We can say the problem now boils down to using these two rows. We can work it out if we just use the second row for odd index rows and the first row for even index rows.

The approach told here will be applicable to all do problems that follow the same pattern, such as Subset Sum, etc.

Code

C++ Code

#include<bits/stdc++.h>
using namespace std;
int main()
 {
  int t;cin>>t;
  while(t--){
      int numberOfItems;cin>>numberOfItems;
      int weightOfKnapsack;cin>>weightOfKnapsack;
      int wt[numberOfItems],val[numberOfItems]; // store the weight and value of each item
      
      //Taking input
      for(int i=0;i<numberOfItems;i++)
          cin>>val[i];
      for(int i=0;i<numberOfItems;i++)
          cin>>wt[i];
     
      //dp array to store the max value which can be achieved at any weight
      int dp[2][weightOfKnapsack+1];
      
      memset(dp,0,sizeof dp); //initialising the dp array to 0
      
      for(int i=0;i<numberOfItems;i++){
          for(int j=0;j<=weightOfKnapsack;j++){
              dp[i&1][j] = max(dp[i&1][j], (i>=1 ? dp[(i-1)&1][j] : 0));
              if(j>=wt[i])
                  dp[i&1][j] = max(dp[i&1][j], (i>=1 ? dp[(i-1)&1][j-wt[i]] : 0)+val[i]);
          }
      }
      
      cout<<dp[(numberOfItems-1)&1][weightOfKnapsack]<<endl;
  }
}
2

3 // number of items

4 // weight knapsack can hold

10 20 30 // value of items

4 5 1 // weight of items

3

3

10 2 3

4 50 60
30 
0

Java Code

import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
    	int t = sc.nextInt();
    	
    	while(t-- > 0){
    	    int numberOfItems = sc.nextInt();
    	    int weightOfKnapsack = sc.nextInt();
    	    
    	    // store the weight and value of each item
    	    int wt[] = new int[numberOfItems];
    	    int val[] = new int[numberOfItems]; 
    	    
    	    //Taking input
    	    for(int i=0;i<numberOfItems;i++)
    	        val[i] = sc.nextInt();
    	    for(int i=0;i<numberOfItems;i++)
    	        wt[i] = sc.nextInt();
    	   
    	    //dp array to store the max value which can be achieved at any weight
    	    int dp[][] = new int[2][weightOfKnapsack+1];
    	    
    	    //initialising the dp array to 0
    	    for(int i=0;i<2;i++) {
    	        for(int j=0;j<weightOfKnapsack+1;j++)
    	            dp[i][j] = 0;
    	    }
    	    
    	    for(int i=0;i<numberOfItems;i++){
    	        for(int j=0;j<=weightOfKnapsack;j++){
    	            dp[i&1][j] = Math.max(dp[i&1][j], (i>=1 ? dp[(i-1)&1][j] : 0));
    	            if(j>=wt[i])
    	                dp[i&1][j] = Math.max(dp[i&1][j], (i>=1 ? dp[(i-1)&1][j-wt[i]] : 0)+val[i]);
    	        }
    	    }
    	    
    	    System.out.println(dp[(numberOfItems-1)&1][weightOfKnapsack]);
    	}
    }
}

 

2

3 // number of items

4 // weight knapsack can hold

10 20 30 // value of items

4 5 1 // weight of items

3

3

10 2 3

4 50 60
30 
0

 

Complexity Analysis

Time Complexity: O(N*W)

where N = number of items

W = weight knapsack can hold

If N = W, time complexity = O(N^2)

Space Complexity: O(W)

where W = weight knapsack can hold, this is the reduced space complexity.

Translate »