背包问题汇总

0-1背包问题

分为最优解和最优解和两种类型。1、组合问题。2、True、False问题。3、最大最小问题。

最优解一般模板为dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]),
空间压缩后为dp[j]=max(dp[j],dp[j-w[i]]+v[i])。
v[i]与边界的设置,根据题目要求进行更改。

最优解dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])

L1049最后一块石头的重量 II与416方法一样,但是转化思路不容易想到
L474 一和零首先1,0的总和只要大于=即可,这里就可以继承了dp[i][j][k] = dp[i - 1][j][k],同时要求max,这里每次循环的时候都要进行比较

最优解和: dp[i] += dp[i-num]
L494目标和,需要掌握一维,二维的转换方法,这里是累加,不再是比较极值了
L879. 盈利计划这里涉及了三维dp,同时又有很多限制条件

组合问题:(并不是0-1背包类型)需要考虑组合顺序,需将target放在外循环,将nums放在内循环
L377组合总和 Ⅳ这里的循环形式是先target放在外面

true or false问题: dp[i] = dp[i] || dp[i-num]
L416分割等和子集包含了整个的推导过程,从二维dp到一维,明白为什么可以照抄

完全背包问题:

需要先了解推导过程,再结合相关的例题,记牢!

L322零钱兑换完全背包问题,这里要求的是价值最小,min类型
L518零钱兑换 II这里包含了完全背包的推导过程,累加过程,
该题注意对比

「力扣」 第 377 题:一个组合的不同排列是一个新的组合。[1, 1, 2]、[1, 2, 2]、[2, 1, 2] 视为为不同的组合。
「力扣」 第 518 题:一个组合的不同排列在结果集中只出现一次,这一点是「背包问题」的特征,拿东西的顺序不重要。[2, 2, 1] 是一个组合,[1, 2, 2] 和 [2, 1, 2] 不是新的组合。这道题和「力扣」第 39 题:组合总和 很像,只不过:
第 39 题问的是所有的组合列表,应该使用 回溯算法 求解;
第 518 题问的是组合有多少种,而不是问具体的解。应该使用 动态规划 求解。

max类型

在这里插入图片描述
关键形式不同的原因在于,一个是累加的求总和的,一个则是求各种可能情况中的最大或者最小值

class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 读第 1 行
        int N = scanner.nextInt();
        int V = scanner.nextInt();

        // 读后面的体积和价值,不超出体积的情况下取得最大价值
        int[] weight = new int[N];
        int[] value = new int[N];

        for (int i = 0; i < N; i++) {
            weight[i] = scanner.nextInt();
            value[i] = scanner.nextInt();
        }

        //初始化第一行
        int[][] dp = new int[N][V + 1];
        // 因为包含价值为 0 的计算,所以 + 1
        for(int i= 0; i * weight[0] <= V; i++){
            dp[0][i * weight[0]] = i * value[0];
        }

        //核心计算
        for(int i= 1; i < N; i++){//个数
            for(int j = 0; j <= V; j++){//总体积
                // 多一个 for 循环,枚举下标为 i 的物品可以选的个数
                for(int k = 0; k *weight[i] <= j; k++){
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * weight[i]] + k * value[i]);
                }
            }
        }

        System.out.println(dp[N-1][V]);
    }
}

上述代码是求体积一定,不超出规定的体积,获得的最高的价值
可进一步精简推导:
在这里插入图片描述
其实,重点记忆框住的部分即可
在这里插入图片描述

0-1背包倒序是为了参考上一行,因为前面的还没有被修改,可当做上一行
完全背包参考本行,只能先把本行中前面的确定了,才能确定后面的值

//三重循环进行优化
        int[][] dp = new int[N + 1][V + 1];
        //这里不再进行初始化,由于都+1
        for(int i = 1; i <=N; i++){
            for(int j = weight[i - 1]; j <= V; j++){
                dp[i][j] = Math.max(dp[i][j], dp[i][j - weight[i - 1]] + value[i - 1]);
            }
        }

        System.out.println(dp[N][V]);

再进行空间的优化:

//空间优化
        int[] dp = new int[V + 1];
        
        for(int i = 1; i <= N; i++){
            for(int j =weight[i - 1]; j <= N; j++){
                //从头正序遍历
                dp[j] = Math.max(dp[j], dp[j - weight[i - 1]] + value[i - 1]);
            }
        }
        return dp[V];
累加类型

L518零钱兑换 II这里包含了完全背包的推导过程,累加过程
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

示例 1:

输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

推导过程如下;
在这里插入图片描述
在这里插入图片描述
(5)即为最终推导的结果,具体过程请看L518零钱兑换 II

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值