01 knapsack problem (explanation of state transition equation)

Posted by DigitHeads on Sat, 01 Jan 2022 19:38:20 +0100

1. Topic introduction:

There are N # items and a backpack with a capacity of V. each item has its own value and can only be selected once. Under the limited backpack capacity, the total value of the loaded items is the largest.

"0-1 knapsack" is a relatively simple dynamic programming problem and the basis of other knapsack problems.

Dynamic programming is a process of continuous decision-making and seeking the optimal solution. "0-1 knapsack" is the continuous decision-making on the ith item. "0-1" just represents the two decisions of no selection and selection.

2. Problem solving code (C + +):

2.1 version 1 2D:

(1) State f[i][j] definition: the optimal solution (maximum value) of the first I items under the backpack capacity J:

The current status depends on the previous status. It can be understood that the decision is made from the initial status f[0][0] = 0. If there are N , items, N decisions are required. Every decision on the I , item, the status f[i][j] is constantly updated from the previous status.
(2) The current knapsack capacity is not enough (J < v [i]) and there is no choice. Therefore, the optimal solution of the first I) items is the optimal solution of the first I − 1 items:

Corresponding code: f[i][j] = f[i - 1][j].
(3) The current backpack has enough capacity to choose, so you need to decide whether to choose the i# item:

Select: f[i][j] = f[i - 1][j - v[i]] + w[i].
Not selected: f[i][j] = f[i - 1][j].
Our decision is how to get the maximum value, so max() is taken in the above two cases.

The code is as follows:

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 1005;
int v[MAXN];    // volume
int w[MAXN];    // value 
int f[MAXN][MAXN];  // F [i] [J], maximum value of the first I items under volume J 

int main() 
{
    int n, m;   
    cin >> n >> m;
    for(int i = 1; i <= n; i++) 
        cin >> v[i] >> w[i];

    for(int i = 1; i <= n; i++) 
        for(int j = 1; j <= m; j++)
        {
            //  If the current backpack capacity cannot hold the ith item, the value is equal to the first i-1 item
            if(j < v[i]) 
                f[i][j] = f[i - 1][j];
            // Yes, you need to make a decision whether to select the ith item
            else    
                f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
        }           

    cout << f[n][m] << endl;

    return 0;
}

2.2 version 2:
To optimize the state f[i][j] to one-dimensional f[j], in fact, only one equivalent deformation is needed.

Why can you deform like this? The state f[i][j] defined by us can obtain any legal I and j optimal solutions, but the problem only needs to obtain the final state f[n][m], so we only need one-dimensional space to update the state.

(1) State f[j] definition: the optimal solution under N , items and knapsack capacity , j ,.

(2) Note that enumerating , j , must start with , m ,.

(3) Why does the enumeration of knapsack capacity need to be in reverse order in one-dimensional case? In two-dimensional case, the state f[i][j] is derived from the state of the previous round of i - 1, and f[i][j] and f[i - 1][j] are independent. After optimization to one-dimensional, if we are still in positive order, f [smaller volume] will be updated to f [larger volume] , it is possible that the state of the i-1 round should have been used, but the state of the {I} round is used.

(4) For example, in the i-th round of one-dimensional state, f[7] is updated from f[4], where f[4] should be f[i - 1][4], but when enumerating j from small to large, f[4] here becomes f[i][4] in the i-th round of calculation. When enumerating knapsack capacity J in reverse order, we find that f[7] is also updated from f[4], but because it is in reverse order, f[4] here It has not been calculated in the {I} round, so the actually calculated f[4] is still f[i - 1][4].

(5) In short, the one-dimensional case needs to update the state f[j] in the positive order. The state calculated above has been "polluted", but there will be no such problem in the reverse order.

The state transition equation is: F [J] = max (f [J], f [J - v [i]] + w [i]).

for(int i = 1; i <= n; i++) 
    for(int j = m; j >= 0; j--)
    {
        if(j < v[i]) 
            f[i][j] = f[i - 1][j];  // Before optimization
            f[j] = f[j];            // After optimization, the line is automatically established and can be omitted.
        else    
            f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);  // Before optimization
            f[j] = max(f[j], f[j - v[i]] + w[i]);                   // After optimization
    }    

In fact, the state is updated only when the enumerated knapsack capacity > = v [i], so we can modify the loop termination condition to further optimize.

for(int i = 1; i <= n; i++)
{
    for(int j = m; j >= v[i]; j--)  
        f[j] = max(f[j], f[j - v[i]] + w[i]);
} 

Supplementary notes on status f[j]
The state definition f[i][j] in two dimensions is the maximum value of the first I items under the backpack capacity j. In one dimension, without the dimension of the first I items, f[j] is the maximum value of the items that have been decided in the first I rounds and the backpack capacity j.

Therefore, after executing the loop structure, f[j] is the maximum value of all items under the backpack capacity j, because all items have been decided. That is, one-dimensional f[j] is equivalent to two-dimensional f[n][j].

2.3 version 3 optimization input
We notice that when processing data, we are an enumeration of items, items, and volumes.

Therefore, we don't have to open two arrays to record volume and value, but input and process at the same time.

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 1005;
int f[MAXN];  // 

int main() 
{
    int n, m;   
    cin >> n >> m;

    for(int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;      // Processing while inputting
        for(int j = m; j >= v; j--)
            f[j] = max(f[j], f[j - v] + w);
    }

    cout << f[m] << endl;

    return 0;
}

Topics: C++ Algorithm Dynamic Programming