【代码随想录】刷题Day44

1.完全背包

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

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

1.针对01背包问题的一维数组,其实每次的遍历背包都是倒叙遍历的,这是为了让分开上下层环,以便能得到不重复出现的最优解。

2.但是完全背包不同,它可以出现重复的物件,那么我们不需要考虑倒叙上下层关系,直接正序遍历就可以满足可出现重复的条件

改写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]);

    }
}

那么关于遍历的顺序问题:

其实我们能发现,这个算法的思想就是由物件和背包的上个状态推出当前的状态(注意,这里说的上一次值得就是背包容量+1或者物件+1的状态,而不是01背包的上一次循环的概念),那么我们其实是知道的,所谓的上一次推出下一次,就是数组的左上角的值不断推出右下角的值。换句话说,不管哪个顺序,二维数组中都是左上角推出右下角,那么无所谓哪个条件先遍历。

// 先遍历物品,在遍历背包
void test_CompletePack(vector& weight,vector* value, int& bagWeight)
{
    vector dp(bagWeight + 1, 0);
    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]);
        }
    }
}

// 先遍历背包,再遍历物品
void test_CompletePack(vector& weight, vector* value, int& bagWeight)
{
    vector dp(bagWeight + 1, 0);
    for (int j = 0; j <= bagWeight; j++)  // 遍历背包容量
    {
        for (int i = 0; i < weight.size(); i++) // 遍历物品
        {
            if (j - weight[i] >= 0) dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
}

2.零钱兑换 II

518. 零钱兑换 II

本题与494. 目标和思路基本一致

1.dp[j]:背包j容量所能达到要求的种类个数

2.dp[i]为i容量下的种类个数,其实有i种可能:当质量为1的物品放进去dp[i-1]有几种,当质量为2的物品放进去dp[i-2]有几种...当质量为i-1的物品放进去dp[1]有几种,这些情况相加。那么我们知道所谓的条件就是:dp[j] += dp[j - nums[i]];

3.不过,此题不需要考虑数据的重复出现问题,也就是说,本题是一道完全背包问题。

4.初始化,dp[0]=1;因为dp[1]条件是dp[0]推出来的,如果dp[0]为0,后面都会变为0。

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

3.组合总和 Ⅳ

377. 组合总和 Ⅳ

1.本题与上一题最大的区别就是:上一题是组合问题,而这题为排列问题

2.解决方法就是本题为背包先遍历,物件后遍历

class Solution {
public:
    int combinationSum4(vector& nums, int target) {
        vectordp(target+1,0);
        dp[0]=1;
        for(int j=0;j<=target;j++)
        {
            for(int i=0;i=0&&dp[j]

4.关于上面两题引出的排列和组合问题

之所以出现这个问题,是因为完全背包不需要考虑物件重复的问题,那么自然就分出是否要考虑物价的存入先后问题。上两题的核心代码都是dp[j]+=dp[j-nums[i]],只不过排列先遍历背包,组合先遍历物件。

为什么这样呢?

其实很好思考:

1.对于组合,先遍历物件,那么外面的循环会拿到1物品,然后依次判断是否满足背包的容量。下一层外面的循环会拿到2物品,依次遍历背包是,天然的一个条件就是1物件要么一定不放,要么已经放进去,那么我们不会再往回走外层循环得到1物件重新放入的机会,其结果就是要么不放1物件,要放就放在2物件的前面,顺序都定死只有唯一的一种,那么结果自然是组合

2.对于排列,先遍历背包,那么外面的循环会先按照1背包来跟所有物件衡量大小,1物件有可能能被放入,2物件也有可能能被放入。那下一次背包2遍历,背包2继承了背包1的一部分,背包2也依次跟所有物件衡量大小,那么就出现了1物件放在2物件的后面的可能

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