动态规划题解法分析

1.0-1背包类型

从有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

递推公式分为两种:

第一种:dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);//力扣Number416
//分割等和子集、最后一块石头的重量等 一维dp数组无须初始化
//对于一维dp数组 j的含义是背包的容量 dp[j]表示容量为j的背包最大能容纳的价值 
//一维dp数组遍历时需要从后向前遍历
    for (int i = 0; i < wLen; i++){
            for (int j = bagWeight; j >= weight[i]; j--){//注意这里的j的范围
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
​
//对于二维dp数组 初始化第一列全为0 第一行从weight[0]开始往后 dp[0][j]都是weight[0]
//i表示从物品0-i中任意挑选 容量为j的背包 dp[i][j]表示能装的最大价值 
//二维dp从前往后遍历
      for (int i = 1; i < weight.length; i++) {
            for (int j = 1; j <= bagSize; j++) {
                if (j < weight[i]) {
                    /**
                     * 当前背包的容量都没有当前物品i大的时候,是不放物品i的
                     * 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
                     */
                    dp[i][j] = dp[i-1][j];
                } else {
                    /**
                     * 当前背包的容量可以放下物品i
                     * 那么此时分两种情况:
                     *    1、不放物品i
                     *    2、放物品i
                     * 比较这两种情况下,哪种背包中物品的最大价值最大
                     */
                    dp[i][j] = Math.max(dp[i-1][j] , dp[i-1][j-weight[i]] + value[i]);
                }
            }
        }

第二种:dp[j] += dp[j - nums[i]];

注意:求装满背包有几种方法类似的题目,递推公式基本都是这样的

如number494目标和问题:给一个数组,一个target,求通过改变+ - 号让结果等于target的种类

//j的含义为容量为j的背包 dp[j]的含义为容量为j的背包装的价值为target的种类数
//一维dp初始化dp[0] = 1; int[] dp = new int[target]//dp[target]表示容量为target的背包能装的种类数(价值)
//递推公式:dp[j] += dp[j - nums[i]];
//对于0-1背包问题 这种递推公式下的遍历方式为从后向前
     for (int i = 0; i < nums.length; i++) {
            for (int j = bagSize; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }

2.完全背包问题

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件

//完全背包核心代码
// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = weight[i]; j <= bagWeight ; j++) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
​
    }
}
遍历顺序:排列与组合
for (int i = 0; i < coins.size(); i++) { // 遍历物品
    for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
        dp[j] += dp[j - coins[i]];
    }
}

假设:coins[0] = 1,coins[1] = 5。

那么就是先把1加入计算,然后再把5加入计算,得到的方法数量只有{1, 5}这种情况。而不会出现{5, 1}的情况。

所以这种遍历顺序中dp[j]里计算的是组合数!

如果把两个for交换顺序,代码如下:

for (int j = 0; j <= amount; j++) { // 遍历背包容量
    for (int i = 0; i < coins.size(); i++) { // 遍历物品
        if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
    }
}

背包容量的每一个值,都是经过 1 和 5 的计算,包含了{1, 5} 和 {5, 1}两种情况。

此时dp[j]里算出来的就是排列数!

可能这里很多同学还不是很理解,建议动手把这两种方案的dp数组数值变化打印出来,对比看一看!(实践出真知)

    public static void main(String[] args) {
        int[] nums = {1,2,5};
        System.out.println(change(5, nums));
    }
    public static int change(int amount, int[] coins) {
        //组合:先物品 后背包
        int[] dp =new int[amount + 1];//容量为amount的背包能装的种类数(价值)
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j < dp.length; j++) {
                dp[j] += dp[j - coins[i]];
                System.out.print(dp[j] + " ");
            }
            System.out.println();
        }
​
        //排列:先背包 后物品
        int[] dp1 = new int[amount+1];
        dp1[0] = 1;
        for (int j = 0; j <= amount; j++) { // 遍历背包容量
            for (int i = 0; i < coins.length; i++) { // 遍历物品
                if (j - coins[i] >= 0) {
                    dp1[j] += dp1[j - coins[i]];
                    System.out.print(dp1[j] + " ");
                }
            }
            System.out.println();
        }
        return dp[amount];
    }

参考文献:

代码随想录 (programmercarl.com)

动态规划之完全背包,装满背包有几种方法?求排列数?| LeetCode:377.组合总和IV哔哩哔哩bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值