代码随想录算法训练营第32天|完全背包理论基础-二维DP数组、518.零钱兑换II、377. 组合总和 Ⅳ、57. 爬楼梯(进阶版)

卡码网52

2025-03-05 17:12:34 星期三

题目描述:卡玛网52
文档讲解:代码随想录(programmercarl)完全背包理论基础-二维DP数组
视频讲解:带你学透完全背包问题! 和 01背包有什么差别?遍历顺序上有什么讲究?

代码随想录视频内容简记

要点

  1. 完全背包和01背包的区别就是完全背包中对物品是可以重复取的,也就是不限制物品的数量。

  2. 在之前的一维滚动数组中,我们为了防止对物品重复取多次所以采用了后序遍历,那么在此基础上,只要改成前序即可变为完全背包

  3. 同时在遍历顺序中,完全背包因为该成了前序,所以实际上无论是先遍历背包还是先遍历物品都可以。当然也只是针对于纯完全背包问题

卡玛网测试

和之前的46基本一摸一样,稍作改动即可

另外需要注意的一点是在初始化的时候别初始化错了,vector<int> dp(bagweight + 1, 0);,需要有一个+1的操作

点击查看代码
# include<iostream>
# include<vector>
using namespace std;
int main() {
    int n, bagweight;
    cin >> n >> bagweight;
    vector<int> weight(n, 0);
    vector<int> value(n, 0);
    for (int i = 0; i < n; i++) {
        cin >> weight[i] >> value[i];
    }

    vector<int> dp(bagweight + 1, 0);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= bagweight; j++) {
            if (j < weight[i]) dp[j] = dp[j];
            else dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    cout << dp[bagweight] << endl;
    return 0;
}

LeetCode518

题目描述:力扣518
文档讲解:代码随想录(programmercarl)518.零钱兑换II
视频讲解:《代码随想录》算法视频公开课:装满背包有多少种方法?组合与排列有讲究!| LeetCode:518.零钱兑换II

代码随想录视频内容简记

这道题和目标和那道题是很相似的,其实核心都是求装满背包有多少种方式。区别就在于本题可以一个物品使用多次,所以是一个完全背包问题。

梳理

  1. 确定dp[j]数组的含义,表示容量为j的背包,可以装满不同物品的组合数有dp[j]个

  2. 确定递推公式,dp[j] += dp[j - coins[i]],和494目标和一样

  3. 初始化dp数组,dp[0] = 1,同样,这里如果为0,后面在进行计算的时候dp[1 - 1] = 0,dp[2 - 2] = 0,就都是0了

  4. 确定遍历顺序。本题需要求得是一个组合数,需要先遍历物品,后便利背包。而不是一个排列数,先遍历背包,后便利物品。

    有什么区别呢?举个例子:

    比如一个coins数组[1, 2],先遍历物品,后便利背包,那么在计算其重量永远都只会是{1, ..., 2},这样,就是1完了才会有2,所以是一个组合数。但如果先遍历背包,再遍历物品,其重量就会是{1...2, 1...2, 1...}会有重复产生,导致出现排列数

  5. 打印dp数组

LeetCode测试

注意这里有一个小细节,就是第28个测试用例会整数超出,所以在定义dp的时候用这个vector<uint64_t> dp(amount + 1, 0);

点击查看代码
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<uint64_t> dp(amount + 1, 0);
        dp[0] = 1;
        // if (amount < 0) return 0;
        for (int i = 0; i < coins.size(); i++) {
            for (int j = 0; j <= amount; j++) {
                if (j < coins[i]) dp[j] = dp[j];
                else dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
};

LeetCode377

题目描述:力扣377
文档讲解:代码随想录(progrmmercarl)377. 组合总和 Ⅳ
视频讲解:《代码随想录》算法视频公开课:装满背包有几种方法?求排列数?| LeetCode:377.组合总和IV

这道题和上面的518一摸一样,就是球的是一个排列数,所以先遍历背包,后便利物品即可。

LeetCode测试

点击查看代码
class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<uint64_t> dp(target + 1, 0);
        dp[0] = 1;
        for (int j = 0; j <= target; j++) {
            for (int i = 0; i < nums.size(); i++) {
                if (j < nums[i]) dp[j] = dp[j];
                else dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
        
    }
};

57. 爬楼梯(进阶版)

题目描述:卡玛网57
文档讲解:代码随想录(programmercarl)57. 爬楼梯(进阶版)

梳理

这个题之前k哥在讲力扣70爬楼梯的时候提到过

  1. 本题可以将一次爬的台阶个数m抽象成物品的价值(同时也是物品的重量),n阶楼梯就可以抽象成背包的容量target

  2. 本题的物品的价值和重量的数组都是一样的,只需要从1到m添加到一个数组即可。

  3. 同时需要注意的是,本题仍然求的是一个排列数问题,需要先遍历背包,再遍历物品

卡玛网测试

点击查看代码
# include<iostream>
# include<vector>
using namespace std;
int main() {
    int n, target;
    cin >> target >> n;
    
    vector<int> steps(n, 0);
    for (int i = 0; i < n; i++) {
        steps[i] = i + 1;
    }

    vector<int> dp(target + 1, 0);
    dp[0] = 1;

    for (int j = 0; j <= target; j++) {//先遍历背包
        for (int i = 0; i < n; i++) {//再遍历物品
            if (j < steps[i]) dp[j] = dp[j];
            else dp[j] += dp[j - steps[i]];
        }
    }

    cout << dp[target] << endl;

    return 0;

}
posted on   bnbncch  阅读(365)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示