算法训练营Day44 第九章 动态规划part6

第一部分 完全背包理论基础

        完全背包问题就是给定背包的容量,并给出一组物品的重量与价值,问这个背包中能装的最大价值是多少,并且每个物品可以使用无限次。与01背包的区别就在于:01背包问题中,每个物品最多只能用一次,而完全背包问题中的背包可以用无数次。

        完全背包是要求从前向后遍历的,这与01背包也是不同的,对于一维数组求01背包问题,应该从后向前遍历,这是因为每个物品只能被放入背包一次,从前向后会导致重复放入(因为递推公式依赖于上一行左侧数据);而完全背包必须从前向后,它的递推公式依赖于这一行的左侧数据。对于纯完全背包问题而言,先遍历物品还是先遍历背包效果是一样的,都能得到正确结果。但是,对于某些题目而言,遍历顺序就变得很关键了!下面两道题目就会说明这个情况。

第一题 518. 零钱兑换 II 

        这道题是一个完全背包中的组合问题,也就是说元素的顺序不会有影响(例如:【1,1,2】与【2,1,1】这两中形式等同)。

按照动归五部曲来解题:

1.确定dp数组下标含义:用dp[j]来表示凑出总金额为j的方式数量;

2.确定递推公式:对于组合问题,公式为:dp[j] += dp[j - nums[i]];

3.初始化dp数组:令dp[0] = 1,其余为0;

4.确定遍历顺序:对于完全背包中的组合问题,应当先遍历物品,后遍历背包容量;并且,要从前向后依次遍历更新数组。

5.打印dp数组:有问题的话就打印dp数组来debug。

根据上述步骤,可以得出如下代码:

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount + 1, 0);
        dp[0] = 1;
        for(int i = 0; i < coins.size(); i++) {
            for(int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
};

第二题 377. 组合总和 Ⅳ

        这道题很明显是一个排列问题,因为题目告诉我们,(1, 1, 2) 和(1, 2, 1)是两个不同的结果。与此同时,可以发现这是一个完全背包问题,背包容量为target,求刚好放满背包的情况有多少种?与上一题非常相似,唯一的不同就在于本题是排列。对于排列,我们应该先遍历背包容量,后遍历物品

按照动归五部曲写出代码如下:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1);
        dp[0] = 1;
        for(int j = 0; j <= target; j++){ //先遍历背包容量
            for(int i = 0; i < nums.size(); i++) { //后遍历物品
                if(j >= nums[i] && dp[j] < INT_MAX - dp[j - nums[i]])
                    dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
    }
};

总结

        今天学习了完全背包相关知识,可以发现,对排列和组合不同要求的题目,遍历顺序极其关键。Day44打卡!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值