Algorithm notes-dynamic programming-1

01 backpack

Problem introduction

The standard 01 backpack problem refers to a backpack with n (int type) items and maximum weight W (int type). The weight array represents the weight of the item, that is, weight[i] represents the weight of the ith item; the value array represents the value of the item, that is, value[i] represents the value of the ith item. Ask which items to pack into the backpack to maximize the total value of the items, and each item can only be loaded once.

For example 1, suppose the weight of the backpack is 4, and the item information is described by the table:

weight value
Item 0 1 15
Item 1 3 20
Item 2 4 30

Problem transformation

If the problem encountered meets the characteristics of the 01 backpack, you can think about whether it can be transformed into a 01 backpack problem

  • Each item (element) can only be used once
  • The backpack is just full
  • The weight of the placed item (element) is the value of the element, and the value is also the value of the element

Solution

Backtracking

Each item is either fetched or not fetched. Use the backtracking method to search all situations. The time complexity is O(2^n)

Dynamic programming

Use the dynamic programming method to solve the problem in five steps. The following are all described with a two-dimensional dp array for example 1.

The first step is to determine the meaning of the dp array

The dp array is shown in the figure.
Insert picture description here
dp[i][j] means to take any item from the subscript [0~i] and put it into a backpack with a capacity of j. The maximum sum of value
i represents the item, and j represents the capacity

  • Note that i is less than n. For example, there are three items in n=3, and when i=1, only items numbered 0 and 1 can be selected and put into the backpack.
  • Note that j is less than or equal to the backpack weight W. The backpack can hold up to W, but the backpack corresponding to the current dp array can hold up to j

The second step is to determine the recurrence formula

Only two directions can derive dp[i][j]

  • Introduced by dp[i-1][j]. The current backpack capacity is j, and the maximum value can only be selected from items numbered [0 ~ i-1]. Now items with item number i are added, dp[i][j] is dp[i-1] [j]
  • Introduced by dp[i-1][j-weight[i]. dp[i-1][j-weight[i]] represents the maximum value of item i when the backpack capacity is j-weight[i]. Now the backpack just vacates the weight of item i, try to add item i. That is, dp[i][j]=dp[i-1][j-weight[i]]+value[i]

So the recurrence formula is

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

The third step of initialization

The initialization of dp array cannot violate the definition
of dp array. It is known by the recursive formula of dp array. The derivation of the dp array is to use the value of the previous row of the dp array and the value of the previous
column. Therefore, the backpack capacity is 0 when the first row and the first column of the dp array are initialized , so the full initialization is 0, that is, dp [i][0]=0, i is 0~n-1
Insert picture description here

Only item 0 can be selected in the first row, and the backpack capacity is gradually increased to W. Therefore, in dp[0][j], when the capacity j is greater than or equal to the weight of item 0, weight[0], dp[0][j] is the value of item 0, and when j is less than that, it is 0.
Insert picture description here
So when initializing the first line, follow the positive order traversal of the recursive formula, the code has two loops, if you want to have only one loop, you should use the reverse traversal

for (int j = W; j >= weight[0]; j--) {
    dp[0][j] = dp[0][j - weight[0]] + value[0];
}

When the loop traversal is initialized, the code must also follow the recurrence formula, but the positive sequence traversal will add item 0 multiple times

for (int j = weight[0]; j <= W; j++) {
    dp[0][j] = dp[0][j - weight[0]] + value[0];
}

The fourth step is to determine the traversal order

The dp array initialized in the third step is as follows.
Insert picture description here
There are two traversal dimensions: items and backpack weights, so there are two layers of for loops. It is okay
to traverse the items first or traverse the backpack weight first, but it is better to traverse the items first.

for(int i = 1; i < n; i++) { // 遍历物品
    for(int j = 0; j <= W; j++) { // 遍历背包容量 
        if (j < weight[i]) dp[i][j] = dp[i - 1][j];
        else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
        
    }
}

The fifth step is to derive the dp array

The final dp array result is as follows, the
Insert picture description here
final result is dp[2][4]

If you modify the judgment condition during traversal:

if (j - weight[i] >= 0)  
  dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

The dp array becomes: the
Insert picture description here
complete code is as follows:

 vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int W = 4; //背包重量
    int n = 3;  //物品数量

    // 二维数组
    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));

    // 初始化 
    for (int j = W; j >= weight[0]; j--) {
        dp[0][j] = dp[0][j - weight[0]] + value[0];
    }

    // weight数组的大小 就是物品个数
    for(int i = 1; i < n; i++) { // 遍历物品
        for(int j = 0; j <= W; j++) { // 遍历背包容量
            if (j - weight[i] >= 0)  dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
        }
        cout << dp[n][W]<<endl;
    }

Optimization

You can use a one-dimensional dp array (rolling array) to solve the 01 knapsack problem.
Because it is found that the data in the previous row can be reused, the data in the previous row of the first type of dp array is directly copied to the next row,
so the dp array can be compressed into a one-dimensional array :
Dp: [0, 15, 15, 20, 35]
dp[j] represents a backpack with a capacity of j, and the value of the item carried can be up to dp[j].
The recurrence formula is

dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

It should be noted that the traversal order of the one-dimensional dp array is the backpack capacity from large to small. In order to ensure that each item is only put in once,
the traversal sequence can only be traverse the items first and then traverse the backpack capacity

vector<int> weight = {1, 3, 4};
vector<int> value = {15, 20, 30};
int W = 4; //背包重量
int n = 3;  //物品数量

// 初始化
vector<int> dp(W + 1, 0);
for(int i = 0; i < n; i++) { // 遍历物品
    for(int j = W; j >= weight[i]; j--) { // 遍历背包容量
       dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    }
}
cout << dp[W] << endl;

Guess you like

Origin blog.csdn.net/MinutkiBegut/article/details/114131230