Dynamic programming - 01 knapsack problem, complete knapsack problem

01 backpack problem

1. Topic

2. Thinking analysis

Let's understand the meaning of the question first. Suppose you come to a treasure cave and you have a backpack in your hand. There are a lot of gold and silver jewels in front of you. The quantity is n, and the capacity of your backpack is limited to v . How do you want to pack it? The value maximum.

Then define a two-dimensional array f[i][j] , which means that the volume of the first i items does not exceed j and the set with the largest value.

Now that the collection is available, how to find the maximum value? We divide the backpack into two cases, including the i-th item and not including the i-th item. First consider the case of not including the item, then the value of this array is the largest The value should be f[i-1][j] and the array containing i cannot be directly obtained. At this time, the curve is used to save the country, and its value should be the maximum value without i plus the value of i, that is, f [ i][j] =f[i-1][jv[i]] +w[i] , the premise is that j is larger than v[i], then the maximum value at this time comes out f[i][j ] =max( f[i-1][j] ,f[i-1][jv[i] ] +w[i]

3. Complete code

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

    static int N=1010;
    static int []v=new int[N];   //体积
    static int []w=new int[N];   //价值
    static int [][]f=new int[N][N];  //在体积 j内前 i个物品的最大价值
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String []s=br.readLine().split(" ");
        int n=Integer.parseInt(s[0]),m=Integer.parseInt(s[1]);
        for (int i = 1; i <=n; i++) {
            String []s1=br.readLine().split(" ");
            v[i]=Integer.parseInt(s1[0]);
            w[i]=Integer.parseInt(s1[1]);
        }
        for (int i = 1; i <=n; i++) {
            for (int j = 1; j <=m; j++) {
                f[i][j]=f[i-1][j]; //  当前背包容量装不进第i个物品,则价值等于前i-1个物品
                if(j>=v[i]) {
                    f[i][j]=Math.max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
                }
            }
        }
        System.out.println(f[n][m]);
    }
}

4. Code optimization (two-dimensional to one-dimensional)

Convert two dimensions to one dimension:

The first dimension is deleted: take it from the first i items.

f[j] means: take items whose total volume does not exceed j, the maximum total value.

Why can it be transformed into one dimension?

Two-dimensional update method: f[i][j] =max( f[i - 1][j] , f[i - 1][j - v[i]] + w[i]);

1. We found that for the next set of i in each cycle, only i-1 will be used to update the current value, and i-2 and previous values ​​will not be used. So during this update, the original update can be deleted, anyway, it will not be used in the future.

So for the update of i, just use an array and directly overwrite it.

2. We found that for each update of j, only the j or jv[i] from the previous i-1 is needed, and the subsequent values ​​will not be used.

So in order to prevent serial changes, we adopt the method of updating from the back to the front, and update i with the original i-1 array.

(If it is updated from front to back, after the previous update, the subsequent value will be updated, so there is no guarantee that the original i-1 array will be used to update i)

How to convert it to one-dimensional?

Use only one array, overwriting the previous array each time.

1. If the item at the current position is not taken, it will be the same as the information at the previous position (the value at this position of the original i-1 array), so there is no need to change it.

2. If the item at the current position is taken, it needs to be maxed out with the information at the previous position (the upper value at this position of the original i-1 array).

Therefore, the update method is: f[j]=max( f[j], f[ j- v[i] ]+ w[i]);

The entire update method is equivalent to:

Every time i++, the f array is covered from the back to the front to see if the value at each position is updated.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

    static int N=1010;
    static int n,m;
    static int []v=new int[N];   //体积
    static int []w=new int[N];   //价值
    static int []f=new int[N];  //拿了总体积不超过j的情况下的价值最大

    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String s[]=br.readLine().split(" ");
        n=Integer.parseInt(s[0]);
        m=Integer.parseInt(s[1]);
        for (int i = 1; i <=n; i++) {
          String st[]=br.readLine().split(" ");
          v[i]=Integer.parseInt(st[0]);
          w[i]=Integer.parseInt(st[1]);
        }

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

        System.out.println(f[m]);
    }
}

complete knapsack problem

1. Topic

2. Thinking analysis

The idea is the same as the 01 backpack, but now an item can be taken multiple times, then add a cycle to simulate being taken multiple times

3. Complete code

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

    static int N=1010;
    static int n,m;
    static int []v=new int[N];   //体积
    static int []w=new int[N];   //价值
    static int [][]f=new int[N][N];  //拿了总体积不超过j的情况下的价值最大

    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String s[]=br.readLine().split(" ");
        n=Integer.parseInt(s[0]);
        m=Integer.parseInt(s[1]);
        for (int i = 1; i <=n; i++) {
          String st[]=br.readLine().split(" ");
          v[i]=Integer.parseInt(st[0]);
          w[i]=Integer.parseInt(st[1]);
        }

        for (int i = 1; i <=n; i++) {
            for (int j = 1; j <=m; j++) {
              for(int k=0 ; k* v[i]<=j ;k++){
                  f[i][j]=Math.max(f[i][j] ,f[i-1][j -k*v[i]]+ k*w[i]);
              }
            }
        }

        System.out.println(f[n][m]);
    }
}

4. Code optimization

We enumerate the internal relationship of the update order:

f[i, j] = max( f[i-1,j], f[i-1,jv]+w, f[i-1,j-2*v]+2*w, f[i- 1,j-3*v]+3*w , .....)
f[i , jv]= max( f[i-1,jv] , f[i-1,j-2*v] + w , f[i-1,j-3*v]+2*w , .....)
From the above two formulas, the following recursive relationship can be obtained:
f[i][j]=max(f[ i,jv]+w , f[i-1][j])

With the above relationship, in fact, the k cycle can be omitted, and the core code is optimized as follows:

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

这个代码和01背包的非优化写法很像啊!!!我们对比一下,下面是01背包的核心代码

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

两个代码其实只有一句不同(注意下标)

f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]); //01背包

f[i][j] = max(f[i][j], f[i][j-v[i]]+w[i]); //完全背包问题

因为和01背包代码很相像,我们很容易想到进一步优化。核心代码可以改成下面这样

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

综上所述,完全背包的最终写法如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {

    static int N=1010;
    static int n,m;
    static int []v=new int[N];   //体积
    static int []w=new int[N];   //价值
    static int []f=new int[N];  //拿了总体积不超过j的情况下的价值最大

    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String s[]=br.readLine().split(" ");
        n=Integer.parseInt(s[0]);
        m=Integer.parseInt(s[1]);
        for (int i = 1; i <=n; i++) {
          String st[]=br.readLine().split(" ");
          v[i]=Integer.parseInt(st[0]);
          w[i]=Integer.parseInt(st[1]);
        }

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

        System.out.println(f[m]);
    }
}

5时间复杂度

感谢你能看完, 如有错误欢迎评论指正,有好的思路可以交流一波,如果对你有帮助的话,点个赞支持下

Guess you like

Origin blog.csdn.net/m0_68055637/article/details/129734843