[dynamic programming] 01 knapsack and its optimization

Posted by cpharry on Wed, 29 Dec 2021 13:27:25 +0100

Title Description

There are N items and a backpack with a capacity of V. The space consumed by putting the i-th item is Ci, and the value obtained is Wi. Solve which items can be loaded into the backpack to maximize the total value without exceeding the capacity.

Input format

Two positive integers in line 1 represent N and V respectively, separated by a space.
In line 2, N positive integers represent Ci, separated by a space.
In line 3, N positive integers represent Wi, separated by a space.
Including: 1 ≤ N ≤ 100, 1 ≤ V ≤ 106, 1 ≤ Ci ≤ 10000, 1 ≤ Wi ≤ 10000.

Output format

A positive integer per line represents the maximum sum of values.

sample input

4 20
8 9 5 2
5 6 7 3

sample output

16

Basic idea: dynamic programming

1. Status and definition

Status 1: [facing the first i items]
Missing this status may cause an item to be selected repeatedly
Status 2: current [remaining space j]
Obviously, there is no room left to determine whether an item can be selected

Summary: DP [i] [j]: the maximum value when facing the first I items and the remaining space is j

2. State transition equation

For the i-th item, there are only two cases: selection and non selection
choice:
If the remaining space is greater than or equal to the space occupied by the item, you can select
If you want to ensure the maximum value of the first i items, you should increase the value of the item based on the maximum value of the first i-1 items, and reduce the volume of the corresponding item:
dp[ i ][ j ] = dp[i-1][ j- c[i] ]+w[i]

Do not select:
If the i-th item is not selected, it means that the i-th item has no impact on the maximum value. The maximum value of the first i-th item is consistent with the maximum value of the first i-1 item
dp[i][j] = dp[ i-1][ j ]

Summary:
Select or not select the larger one
dp[ i ] [ j ] = max(dp [ i-1] [ j-c[i] ]+w[i],dp [ i-1][ j ])

3. Initial value

The initial values are all 0

Code 0:
/***
Dynamic programming 2D array version: 
1.Status:
	Status 1: first i items 
	State 2: backpack space j
	dp[i][j]: The maximum value items that can be loaded when facing the first i items and the space is j 
2.equation 
	dp[i][j] = max(dp[i-1][j-C[i]]+W[i],dp[i-1][j]); 
3.initial value
	dp[i][j] = 0
	 
**/

#include <bits/stdc++.h>
#define maxlen 1005

using namespace std;
int n,v;
int c[102];
int w[102];
int dp[102][int(1e6+2)];

int main() {

	cin>>n>>v;
	
	for(int i = 1 ;i<=n ;i++){
		cin>>c[i];
	}
	
	for(int i = 1 ;i<=n ;i++){
		cin>>w[i];
	}
	
	for(int i = 1 ;i<=n; i++){
		for(int j = 1; j<=v ;j++)
		{
			//By default, the ith item is not selected first 
			dp[i][j] = dp[i-1][j];
			//If you can select, compare the selected and unselected sizes 
			if(j-c[i]>=0)
				dp[i][j] = max(dp[i-1][j-c[i]]+w[i],dp[i][j]);
		}		
	}
	
	for(int i = 1 ;i<=n ;i++){
		for(int j = 1; j<=v ;j++) 
			cout<<dp[i][j]<<" ";
		cout<<endl;
	}
	
	cout<<dp[n][v]<<endl; 
   return 0;
}

Improvement 1

In fact, by the state transition equation
dp[ i ] [ j ] = max(dp [ i-1] [ j-c[i] ]+w[i],dp [ i-1][ j ])
And dp two-dimensional array values are easy to find:

  |1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20
-|------------------------------
1 |0  0  0  0  0  0  0  5  5  5  5  5  5  5  5  5  5  5  5  5
2 |0  0  0  0  0  0  0  5  6  6  6  6  6  6  6  6  11 11 11 11
3 |0  0  0  0  7  7  7  7  7  7  7  7  12 13 13 13 13 13 13 13
4 |0  3  3  3  7  7  10 10 10 10 10 10 12 13 15 16 16 16 16 16

The data of row i only depends on the value of row i-1, so we only need to save the values of row i-1 and row i, that is, two one-dimensional arrays dp1 [] and dp2 []
DP1 [J] is equivalent to DP [I-1] [J]
DP2 [J] is equivalent to DP [i] [J]
New equation: dp2[j] = max(dp1[j-c[i]]+w[i],dp2[j])

Code 1
/**
Dynamic programming two-dimensional array version:
4 20
8 9 5 2
5 6 7 3
0  0  0  0  0  0  0  5  5  5  5  5  5  5  5  5  5  5  5  5
0  0  0  0  0  0  0  5  6  6  6  6  6  6  6  6  11 11 11 11
0  0  0  0  7  7  7  7  7  7  7  7  12 13 13 13 13 13 13 13
0  3  3  3  7  7  10 10 10 10 10 10 12 13 15 16 16 16 16 16
16
 It can be seen from dp array and equation that only two rows of data are involved each time, and the data of i only depends on row i-1 
Therefore, two one-dimensional arrays can be used to store the data of line i-1 and line i respectively 
**/
 

#include <bits/stdc++.h>
#define maxlen 1005

using namespace std;
int n,v;
int c[102];
int w[102];
//Save the data in line i-1 
int dp1[int(1e6+2)];
//Save the data of line i 
int dp2[int(1e6+2)];
int main() {

	cin>>n>>v;
	
	for(int i = 1 ;i<=n ;i++){
		cin>>c[i];
	}
	
	for(int i = 1 ;i<=n ;i++){
		cin>>w[i];
	}
	//dp1[j] : dp[i-1][j] 
	//dp2[j] : dp[i][j]
	for(int i = 1; i<=n;i++){
		for(int j = 1 ;j<=v ;j++){
			dp2[j] = dp1[j];
			if(j-c[i]>=0)
				dp2[j] = max(dp1[j-c[i]]+w[i],dp2[j]);
		}
		//Assign dp2 to dp1 and save the value of the previous line 
		for(int k = 1 ;k<=v ;k++)
			dp1[k] = dp2[k];
	}
	cout<<dp2[v]<<endl;
	//or
	//cout<<dp1[v]<<endl;
   return 0;
}
Improvement 2:

From the state transition equation: dp[i][j] = max(dp[i-1][j-c[i]]+w[i],dp[i-1][j-1])
Or dp2[j] = max(dp1[j-c[i]]+w[i],dp1[j])

The data in row i is updated in the order of column (space) from small to large. If it is updated in place
This method will cause the data with a small number of columns in the array to be overwritten.

If the columns are enumerated in descending order, the data with a large number of columns is updated,
Although the data with small column number order is used to update the data with large column number, it is not destroyed.
Using this feature, we can represent dp1 [] and dp2 [] as one dp []
The new equation is: dp[j] = max(dp[j-c[i]]+w[i],dp[j]), but j is from v to 0

For example, take the generation of row 2 as an example: first, use dp [] to store the data of row 1
(since the initial value of the array is 0 when the first row is generated, the process of generating data in situ cannot be reflected, so it is assumed that the first row has been generated.)

When i = 1:
0 dp[]: 0 0 0 0 0 0 0 5 5 5 5 5 5 5 5 5 5 5 5 5

i = 2 , j = v:
dp[v] = dp[v-c[2]]+w[2] = dp[11]+6 = 11
1 dp[]: 0 0 0 0 0 0 0 5 5 5 5 5 5 5 5 5 5 5 5 11
----------------------------------------------------------^

i = 2 , j = v-1
dp[v-1] = dp[v-1-c[2]]+w[2] = dp[10]+6 = 11
2 dp[]: 0 0 0 0 0 0 0 5 5 5 5 5 5 5 5 5 5 5 11 11
-------------------------------------------------------^

i = 2 , j= v-2
dp[v-2] = dp[v-2-c[2]]+w[2] = dp[9]+6 = 11
3 dp[]: 0 0 0 0 0 0 0 5 5 5 5 5 5 5 5 5 5 11 11 11
----------------------------------------------------^

i = 2 , j =v-3
dp[v-3] = dp[v-3-c[2]]+w[2] = dp[8]+6 = 11
4 dp[]: 0 0 0 0 0 0 0 5 5 5 5 5 5 5 5 5 11 11 11 11
--------------------------------------------------^

i = 2 , j = v-4
dp[v-4] = dp[v-4-c[2]]+w[2] = dp[7]+6 = 6
5 dp[]: 0 0 0 0 0 0 0 5 5 5 5 5 5 5 5 6 11 11 11 11
-----------------------------------------------^

By analogy, it can be observed that when j enumerating in reverse order, even if the data of the next column is generated in place, no information will be lost

Code 2:
#include <bits/stdc++.h>
#define maxlen 1005

using namespace std;
int n,v;
int c[102];
int w[102];
int dp[int(1e6+2)];
int main() {

	cin>>n>>v;
	
	for(int i = 1 ;i<=n ;i++){
		cin>>c[i];
	}
	
	for(int i = 1 ;i<=n ;i++){
		cin>>w[i];
	} 
	for(int i = 1; i<=n;i++){
		//Note: enumeration in reverse order 
		for(int j = v ;j>=0 ;j--){
			if(j-c[i]>=0)
				dp[j] = max(dp[j-c[i]]+w[i],dp[j]);
		}
	}
	cout<<dp[v]<<endl;
   return 0;
}

Topics: Algorithm Dynamic Programming