01背包问题模板与完全背包模板问题

01背包

牛客链接【模板】01背包_牛客题霸_牛客网 (nowcoder.com)

我们都根据几步进行分析

1.状态表示

2.状态转移方程

3.初始化

4.填表顺序

5.返回值

根据以上来分析,并由此进行空间优化

01背包第一问

1.状态表示

根据一些题目的经验

我们设置dp[i]来表示在前i个物品里,选出最大价值(发现无法表示体积)所以应该是要用二维dp来表示,因为没有变量来表示体积的状态

因此使用dp[i][j]表示在前i个物品里,体积为j里,选出最大价值

2.状态转移方程

我们以i选不选来分类讨论

但是要注意一些细节问题!!!

首先就是需要j - v[i]一定要满足 >= 0,不然会发生越界情况

这样就可以表示出所有的状态,取两者最大值填入表中即可

3.初始化

第一行可以很清楚的知道,因为从0个物品里选择总体积为j的最大价值,都是填0

第一列,从i个物品里选择总体积为0的最大价值,也都填0,就不需要进行初始化了,直接创建即可

4.填表顺序

根据状态转移方程可以看出时从上到下填写表

从左到右从右到左都可以,我们习惯从左向右(和后面的优化有一定的关系)

5.返回值

直接返回dp[n][v]

n是前n个物品,v是背包体积

01背包第二问

1.状态表示

和上一题一样

2.状态转移方程

和上一题一模一样,但是多了一个当无法满足体积要求的状态

细节!!!

1.我们使用dp[i][j] == -1 来表示这个体积无法在前i个物品中选出

2.首先就是需要j - v[i]一定要满足 >= 0,不然会发生越界情况,其次就是需要前一个体积必须要存在,所以前一个体积不能等于 -1 

3.初始化

第一行可以很清楚的知道,因为从0个物品里选择总体积为j的最大价值,除了第一个以外,其他的0个物品中选择为j体积都无法达到,所以要等于-1

第一列,从i个物品里选择总体积为0的最大价值,都填0,就不需要进行初始化

4.填表顺序

和前一问一样

5.返回值

直接返回dp[n][v]

n是前n个物品,v是背包体积

可以写出代码

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       int n = sc.nextInt();//数量
       int v = sc.nextInt();//背包体积
       int[][] arr = new int[n + 1][2];//存储物品体积0与价值1
       for(int i = 1;i <= n;i++){
        arr[i][0] = sc.nextInt();
        arr[i][1] = sc.nextInt();
       }
       int[][] dp = new int[n + 1][v + 1];
       for(int i = 1;i <= n;i++){
            for(int j = 1;j <= v;j++){
                dp[i][j] = dp[i - 1][j];
                if(j - arr[i][0] >= 0){
                    dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - arr[i][0]] + arr[i][1]);
                }
            }

       }
       System.out.println(dp[n][v]);
        for(int i = 1;i <= n;i++){
            Arrays.fill(dp[i],0);
        }
        for(int i = 1;i <= v;i++){
            dp[0][i] = -1;
        }
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= v;j++){
                dp[i][j] = dp[i - 1][j];
                if(j - arr[i][0] >= 0 && dp[i - 1][j - arr[i][0]] != -1){
                    dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - arr[i][0]] + arr[i][1]);
                }
            }
        }
        System.out.println(dp[n][v] == -1 ? 0 : dp[n][v]);
    }
}










空间优化

由于每次填表的时候只需要用到前一列的变量其他的不需要使用,并且是只需要用到左上方的变量,我们可以使用滚动数组的方式来进行优化

1.遍历需要从右向左

2.将dp的行给删掉

3.不要去解释优化后的变量,自己删一遍感受一下

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       int n = sc.nextInt();//数量
       int v = sc.nextInt();//背包体积
       int[][] arr = new int[n + 1][2];//存储物品体积0与价值1
       for(int i = 1;i <= n;i++){
        arr[i][0] = sc.nextInt();
        arr[i][1] = sc.nextInt();
       }
       int[] dp = new int[v + 1];
       for(int i = 1;i <= n;i++){
            for(int j = v;j >= arr[i][0];j--){
                dp[j] = Math.max(dp[j],dp[j - arr[i][0]] + arr[i][1]);
            }

       }
        System.out.println(dp[v]);
        Arrays.fill(dp,0);
        for(int i = 1;i <= v;i++){
            dp[i] = -1;
        }
        for(int i = 1;i <= n;i++){
            for(int j = v;j >= arr[i][0];j--){
                if(dp[j - arr[i][0]] != -1){
                    dp[j] = Math.max(dp[j],dp[j - arr[i][0]] + arr[i][1]);
                }
            }
        }
    System.out.println(dp[v] == -1 ? 0 : dp[v]);
    }
}










完全背包

将01背包中的物品从1个变成无数个

完全背包_牛客题霸_牛客网 (nowcoder.com)

完全背包第一问

1.状态表示

和01背包是一样的,这里不过多阐述

2.状态转移方程

这里分析思路也是像01背包一模一样

分析选不选i之后的多种情况,取其中的最大值,可以使用多一个for循环来判断,前提是j - nw[i]要大于等于0,然后再填表,这里直接给出数学证明

将dp[i][j]表示成以上形式

其他的细节判断与01背包问题一致(学明白01背包再来看完全背包是很简单的)

3.初始化

第一行可以很清楚的知道,因为从0个物品里选择总体积为j的最大价值,都为0

第一列,从i个物品里选择总体积为0的最大价值,可以让表自己填,因为会判断是否j - nw[i]要大于等于0

4.填表顺序

与01背包不同

从上到下,从左到右填表

5.返回值

直接返回dp[n][v]

n是前n个物品,v是背包体积

可以写出代码

完全背包第二问

1.状态表示

和01背包是一样的,这里不过多阐述

2.状态转移方程

与第一问一样,不同的是无法选出体积的需要标记成-1,此外还需要判断是否为-1

3.初始化

第一行可以很清楚的知道,除了第一个为0,其他的都选不到,全部填-1

第一列,从i个物品里选择总体积为0的最大价值,可以让表自己填,因为会判断是否j - nw[i]要大于等于0

4.填表顺序

与上一问一致

从上到下,从左到右填表

5.返回值

直接返回dp[n][v]

n是前n个物品,v是背包体积

可以写出代码

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param v int整型 
     * @param n int整型 
     * @param nums int整型ArrayList<ArrayList<>> 
     * @return int整型ArrayList
     */
    public ArrayList<Integer> knapsack (int v, int n, ArrayList<ArrayList<Integer>> nums) {
        // write code here
        ArrayList<Integer> ret = new ArrayList<>();
        int[][] dp = new int[n + 1][v + 1];
        for(int i = 1;i <= n;i++){
            for(int j = 0;j <= v;j++){
                dp[i][j] = dp[i - 1][j];
                if(j - nums.get(i - 1).get(0) >= 0){
                    dp[i][j] = Math.max(dp[i][j],dp[i][j - nums.get(i - 1).get(0)] + nums.get(i - 1).get(1));
                }
            }
        }
        ret.add(dp[n][v]);
        for(int i = 1;i <= n;i++){
            Arrays.fill(dp[i],0);
        }
        for(int i = 1;i <= v;i++){
            dp[0][i] = -1;
        }
        for(int i = 1;i <= n;i++){
            for(int j = 0;j <= v;j++){
                dp[i][j] = dp[i - 1][j];
                if(j - nums.get(i - 1).get(0) >= 0 && dp[i][j - nums.get(i - 1).get(0)] != -1){
                    dp[i][j] = Math.max(dp[i][j],dp[i][j - nums.get(i - 1).get(0)] + nums.get(i - 1).get(1));
                }
            }
        }
        ret.add(dp[n][v] == -1 ? 0 : dp[n][v]);
        return ret;
    }
}

空间优化 

我们还是基于填表顺序,来进行滚动数组的空间优化

从左向右从上往下,填完第一行时只有第一行最上面的才有用,我们从左向右填表,从上往下即可变成滚动数组的形式

1.不要去解释改变滚动数组的状态

2.自己写完代码后去删一下,删除行数,还有改变一下遍历的头尾即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值