Backpack problem-2. Simple 01 backpack

Simple 01 knapsack problem

#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

const int maxn=1010,maxv=1010;
int v[maxn],w[maxn];  //v体积,w价值
int dp[maxv];

int main()
{
    
    
    int N,V;
    read(N,V);
    for (int i=1;i<=N;i++)  read(v[i],w[i]);
    
    for (int i=1;i<=N;i++)
        for (int j=V;j>=v[i];j--)"更新当前物品在所有背包体积下的最优选择,不能选择的就不更新了,直接继承"
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
            
    printf("%d",dp[V]);
    return 0;
}

Title description

There are N items and a backpack with a capacity of V.Each item can only be used once.

The volume of the i-th item is vi, and the value is wi.

Find out which items to pack into the backpack, so that the total volume of these items does not exceed the backpack capacity, and the total value is the largest. Output the maximum value.

Input format The
two integers in the first line, N and V, are separated by a space, indicating the number of items and the volume of the backpack respectively.

Next, there are N rows, each with two integers vi and wi, separated by a space, indicating the volume and value of the i-th item .

Output format
Output an integer, which represents the maximum value.

Data range
0<N,V≤1000
0<vi,wi≤1000

Input sample

4 5
1 2 Volume, value
2 4
3 4
4 5

Sample output:

8

analysis:

For each object, there are two states: selected or unselected, and it must be possible not to select this item, but whether this item can be selected depends on the current backpack size:

  1. If the volume of the object is less than or equal to the current backpack volume, there are two states: selected and unselected;
  2. If the volume of the object is larger than the current backpack volume, there is no choice but to deselect this state.

Two-dimensional array implementation

#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

const int maxn=1010,maxv=1010; //最多1000个物品,价值最大为1000
int v[maxn],w[maxn]; //记录每个物品的体积v还有价值w,从下标1开始存物品,方便和dp数组对应
int dp[maxn][maxv]; //选择前N个物品,体积最大为V

int main()
{
    
    
    int N,V;
    read(N,V);
    for (int i=1;i<=N;i++)  read(v[i],w[i]);  "从下标1开始存储物品,和dp数组对应"
"开始递推过程,递推方程:dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);"
    for (int i=1;i<=N;i++)  //先循环物品,再循环体积,先行后列。
        for (int j=1;j<=V;j++) {
    
     //从0开始最好,0~V
            dp[i][j]=dp[i-1][j];   "不选第i个物品是必然成立的,如果他不能选择第i个物品就直接继承上一行"
            if (j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);   
        }          "在可以选择第i个物品的条件下,判断是否要选择第i个物品"
    printf("%d",dp[N][V]);
        
    return 0;
}

dp[i][j] represents the maximum value of various options under the premise of the first i items and the weight of the backpack is j:
if the item can be put into the backpack, the dynamic transfer equation is dp[i][j]=max( dp[i-1][j],dp[i-1][ j-v[i] ]+ w[i] ), j>=w[i]
If the item cannot be put into the backpack dp[i][j]=dp[i-1][j] , j<w[i]

However, it is necessary for each item to choose not to put it in the backpack, so you can also choose the writing in the code above.

Recursive core: When selecting the optimal solution of the first i items, you only need to determine whether to choose the i-th item based on the optimal solution of the first i-1 items. Under backpacks of different capacities, whether It is not the same to choose the i-th item.

The final result: the two-dimensional dp table records the maximum volume of the item selection in all cases, we only need the final maximum value when the first N items are selected and the backpack volume is V, that isdp[N][V]

Recycle items first, then volume? Recycle the volume first, then recycle the items

We observe this dynamic transfer equation,In the i-th row, only the data of the previous row is needed, that is, the i-1th row; in the j-th column, it only depends on the data in the j-th column and its left column, that is, the first j column.That update dp[i,j]when each will only use the first row dp i-1 two-dimensional table, from dp[i-1,0]the dp[i-1][j]data, which provides the basis for our subsequent optimization, and we have to consider the order of traversal problem :

  1. Traverse from top to bottom , first row and then column, that is, first circulate the items, and then circulate the volume. A backpack with V capacity under each item needs to determine the best choice and update it by row. Each row down represents one more item.
  2. Traverse from left to right , column first, then row, that is, the volume is circulated first, and then the items are circulated. There are N different backpacks under each volume. When walking the first row, select the first item, and when walking the second row, select the first two items. The data in the first row is required , ..., until the first row . For N rows, the data of the N- 1th row is needed , so that the V column is always looped.

But pay attention to the initialization problem in the two traversal modes:

  1. Traverse from top to bottom, When seeking dp[i][j], only use all the data in the i-1th row . In order to prevent the array from crossing the boundary, the processing cannot be started from the 0th row. It must start from the 1st row, so the 0th row must be initialized to 0 first, that is, not Choose the maximum value of any item in various sizes, which is 0. Then, the items are enumerated from 1 and the backpack volume is enumerated from 0.
for (int i=1;i<=N;i++) 
        for (int j=0;j<=V;j++)

The reason why the volume of the backpack enumerated from 0 to V, when this is to prevent v[i]==jthe time, jv [i] = 0, the situation has returned to column 0, column 0 must each row need to be maintained, in fact, when When j is 0, only dp[i][0]=dp[i-1][0], (unless there is an item with a volume of 0), so it dp[i][0]will eventually be equal to dp[0][0]the 0 initialized in row 0, so the 0th column can be initialized directly to 0 during initialization, then items and backpacks The volume can be directly enumerated from 1, which is shown in the above code:

for (int i=1;i<=N;i++)  
        for (int j=1;j<=V;j++)
  1. Traverse from left to rightSeeking dp[i][j], it may use the j-th column on the left before the first data row i-1, including the newly updated line datadp[i-1][j] , in order to prevent the array bounds (-1) must backpack volume enumerated from the beginning, Items are enumerated from 1 , and must be enumerated from left to right, from top to bottom. The next row needs to use the data of the previous row . Therefore, when initializing, the 0th row and the 0th column must be initialized to 0.
for (int j=1;j<=V;j++) 
        for (int i=1;i<=N;i++)

Initialize the meaning of row 0: Row 0 represents nothing, so no matter what the size of the backpack, the value is 0, so dp[0][j]=0;
initialize the meaning of the first column: No matter what item is selected, the size of my backpack is limited to 0, so you The value of must also be 0. and sodp[i][0]=0;

Rolling array implementation

We use the method of recycling the items first, and then the volume of the backpack , because he will only use the data of the previous row each time, then define a two-dimensional array with 2 rows and V columns:int dp[2][V];

If it is to recycle the backpack volume first, and then recycle the items , after calculating dp[i-1][j], when calculating dp[i][j], he may use the i-1th row on the first j column For the data, you must even use the dp[i-1][j] just found, and this requires more than two columns to maintain, so only the first loop method can be optimized with a rolling array.

  1. You can define a variable p by XOR operation, if p=0, then p^1=1;
    if p=1, then p^1=0, so you can switch repeatedly between the 0th row and the 1st row.
  2. Through the AND operation, traversing the i of the item can represent the number of rows, and i&1 can take out the lowest bit. When it is an even number, i is 0; when it is an odd number, i is 1, so as to determine whether the 0th row or the 1st row is.
#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

const int maxn=1010,maxv=1010;
int v[maxn],w[maxn];  //v体积,w价值
int dp[2][maxv];

int main()
{
    
    
    int N,V;
    read(N,V);
    for (int i=1;i<=N;i++)  read(v[i],w[i]);
    //dp递推,预处理:0行0列初始化为0,体积可以从1处理
    int p=0;   "每次循环结束时,p指向处理完后的那一行,上次处理的是第0行。"
    for (int i=1;i<=N;i++) {
    
    
        for (int j=1;j<=V;j++) {
    
      //p^1表示当前处理的行
            if (j>=v[i]) dp[p^1][j]=max(dp[p][j],dp[p][j-v[i]]+w[i]);   //可以选择物品i,在是否选择之间抉择
            else  dp[p^1][j]=dp[p][j];     //不能选择物品[i],直接继承上一行
        }
        p^=1; "p=p^1; 在在第0行和第1行之间来回切换"
    }
    printf("%d",dp[p][V]);
    
    return 0;
}

One-dimensional array implementation

The optimization of the rolling array is the nature of using only the data of the i-1th row through the calculation of the i-th row, so let's be more specific: when
updating dp[i,j], only the i-1th row in the dp two-dimensional table is used each time. from dp[i-1,0]to dp[i-1][j]the data.

Therefore, it can be optimized as a one-dimensional array. It can be known that the smaller the backpack volume j, the more times it will be used, and it will be used between the backpack volume j and V in the next layer. Therefore, the second level of the for loop is in reverse order . When seeking dp[i][j], start with j=V first, because dp[i-1][V] will not be excluded from dp[i][V] Dp[i][j] is used, dp[i-1][V] is useless after this update, so dp[i][V] can overwrite dp[i-1][ V], and then update in descending order.

The condition for the end of the judgment is whether the backpack capacity j is greater than the volume v[i] of the items to be loaded into the backpack, because when j is less than v[i], it is directly known from the foregoing that dp[i][j]=dp[i -1][j], inherit the data of the previous row, because we use a one-dimensional array, it can be directly inherited.

#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

const int maxn=1010,maxv=1010;
int v[maxn],w[maxn];  //v体积,w价值
int dp[maxv];

int main()
{
    
    
    int N,V;
    read(N,V);
    for (int i=1;i<=N;i++)  read(v[i],w[i]);
    
    for (int i=1;i<=N;i++)
        for (int j=V;j>=v[i];j--)"更新当前物品在所有背包体积下的最优选择,不能选择的就不更新了,直接继承"
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);  //进入for循环的都是可以选择物品i的情况
            
    printf("%d",dp[V]);
    return 0;
}

Guess you like

Origin blog.csdn.net/HangHug_L/article/details/114228223