算法刷题打卡038 | 动态规划6

完全背包问题

相同的问题背景下,完全背包和01背包最大的区别是,01背包中的每个物品只有一个(只能使用一次),而完全背包的物品有无限个。之前学习01背包的一维滚动数组时就了解到,背包的遍历顺序影响到每个物品被用到的次数,只有倒序遍历才符合01背包的特性,而正序遍历刚好就是完全背包的情况,同一个物品可以取用无限次数。

理解完全背包之后,代码实现就很简单了,基本就是01背包的代码调整一下遍历顺序。关键是如何将问题抽象为背包问题,并且明确状态如何递推。

LeetCode 518 零钱兑换

题目链接:518. 零钱兑换 II - 力扣(Leetcode)

零钱兑换题目描述中明确了:每一种面额的硬币有无限个,显然是完全背包,同时要求的是不同的组合种数,dp的结果应该累加背包容量遍历过程中的值:

class Solution {
public:
    int change(int amount, vector& coins) {
        int n = coins.size();
        vector dp(amount + 1);
        dp[0] = 1;  // 组合数不能忘记初始化
        for (int i=0; i

而在这一题中,物品和背包的遍历顺序也变得很重要了,这是区别组合和排列问题的关键。先遍历物品时,每个物品的相对顺序是固定的,求方法数的时候就不会出现重复的组合;而先遍历背包容量再遍历物品,相同组合中的元素可能会以不同的顺序被用到,构成不同的排列。

LeetCode 377 组合总和IV

题目链接:377. 组合总和 Ⅳ - 力扣(Leetcode)

这一题与零钱兑换恰好可以对照着看,这里要求的是排列的种数,因此需要先遍历背包容量:

 

class Solution {
public:
    int combinationSum4(vector& nums, int target) {
        int res = 0;
        int n = nums.size();
        vector dp(target + 1);
        dp[0] = 1;
        for(int j=0; j<=target; j++){
            for (int i=0; i= nums[i]) dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
    }
};

部分测试用例的dp元素值相加时会出现int溢出,将dp数组定义为unsigned int解决问题(dp数组元素都是正数)。

你可能感兴趣的:(刷题,算法,动态规划)