代码随想录算法训练营DAY44|C++动态规划Part6|完全背包理论基础、518.零钱兑换II、377. 组合总和 Ⅳ

文章目录

  • 完全背包理论基础
    • 完全背包问题的定义
    • 与01背包的核心区别
    • 为什么完全背包的循环顺序可以互换?
    • CPP代码
  • ⭐️518.零钱兑换II
    • 思路
    • CPP代码
  • ⭐️377. 组合总和 Ⅳ
    • 思路
    • CPP代码
  • 扩展题

完全背包理论基础

卡码网第52题

文章链接:完全背包理论基础

视频链接:带你学透完全背包问题!

完全背包问题的定义

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

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

重量价值
物品0115
物品1320
物品2430

每件商品有无限个,问背包能背的物品的最大价值是多少?

与01背包的核心区别

先看01背包遍历的核心代码

for (int i = 0; i < weight.size(); i++) {for (int j = bagweight; j >= weight[i]; j++) {dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}
}

01背包的内循环是从大到小遍历的,目的就是为了保证每个物品只被添加一次;

那么对于完全背包问题的物品可以多次添加,所以要从小到大去遍历,这样的遍历方式使得每个物品可以在更新当前容量 j 的时候重复利用之前已经计算过的结果(也就是说在同一个 i 循环中,dp[j] 可以从 dp[j - weight[i]] 中获得更新,而 dp[j - weight[i]] 可能刚刚在本轮循环中被更新过),从而允许每个物品被多次选取。

//先遍历物品,再遍历背包
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]);}
}

为什么完全背包的循环顺序可以互换?

在0-1背包理论基础(一)、0-1背包理论基础之滚动数组(二)文章中已经指出在01背包问题中,一维dp数组的两个for循环一定是先遍历物品,再遍历背包容量。

在完全背包问题中,对于一维dp数组来说,其实两个for循环嵌套顺序是无所谓的!

1. 先遍历物品,再遍历背包容量

for (int i = 0; i < n; i++) {      // 遍历物品for (int j = weight[i]; j <= bagWeight; j++) { // 遍历背包容量dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}
}

我们可以看出,每次考虑一个物品,然后更新所有可能的背包容量。由于是正序更新,所以 dp[j] 可以反复从 dp[j - weight[i]] 获取价值,实现了物品的重复选择。状态图展示如下:

2. 先遍历背包容量,再遍历物品

for (int j = 0; j <= bagWeight; j++) {      // 遍历背包容量for (int i = 0; i < n; i++) { // 遍历物品if (j >= weight[i]) {dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}}
}

在这种情况下,每个背包容量都尝试添加所有可能的物品。这样的循环同样可以正常工作,因为每个 dp[j] 都会考虑是否加入每个物品 i,并且仍然可以通过 dp[j - weight[i]] 反复获得价值,从而实现物品的重复选择。

CPP代码

这里是卡码网第52题问题的答案。

#include <bits/stdc++.h>
using namespace std;int numsMaterials;
int bagWeight;void solve () {vector<int> weights(numsMaterials, 0);vector<int> values(numsMaterials, 0);int weight, value;for (int i = 0; i < numsMaterials; i++) {int weight, value;cin >> weight >> value;weights[i] = weight;values[i] = value;}vector<int> dp(bagWeight + 1, 0);for (int i = 0; i < numsMaterials; i++) {for (int j = weights[i]; j<= bagWeight; j++) {dp[j] = max(dp[j], dp[j - weights[i]] + values[i]);}}cout << dp[bagWeight] <<endl;
}int main () {cin >> numsMaterials >> bagWeight;solve();return 0;
}

⭐️518.零钱兑换II

力扣题目链接

文章链接:518.零钱兑换II

视频链接:装满背包有多少种方法?组合与排列有讲究!| LeetCode:518.零钱兑换II

状态:「错误点」
1. 关于dp[0]应该初始化为1,因为系统后台默认的是dp[0]应该等于1
2. 第一个遍历物品的时候肯定从零开始啊!不知道为什么第一次写的时候从1开始了。

思路

首先可以确定bagWeight=amount=5,再一个weight=value=coins。再一个本题和纯完全背包问题还不一样,纯完全背包是凑成背包最大价值是多少,而本题是要求凑成总金额的物品组合个数!

  • 确定dp数组以及下标的含义

dp[j]:凑成总金额j的货币组合为dp[j]

  • 确定递推公式

dp[j]就是所有的dp[j-coins[i]]情况相加。我们已经在这篇文章中讨论过该类问题:494.目标和

  • dp数组如何初始化

卡哥文章里写了,后台测试数据是默认,amount = 0 的情况,组合数为1的

也就是说dp[0]=1——凑成总金额0的货币组合数为1。

下标非0的dp[j]初始化为0,这样累计加dp[j - coins[i]]的时候才不会影响真正的dp[j]

  • 确定遍历顺序

对于一个纯背包问题来说,遍历顺序并不重要,因为他是一个排列问题.

但是本题中是一个明显的组合问题,比如说我们可以有一种分配方法是{1, 5}但是绝对不能再有{5, 1}。所以对于遍历顺序而言一定是 外层for循环遍历物品(钱币),内层for遍历背包(金钱总额)的情况

for (int i = 0; i < coins.size(); i++) { // 遍历物品for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量dp[j] += dp[j - coins[i]];}
}
  • 举例推导dp数组

输入: amount = 5, coins = [1, 2, 5] ,dp状态图如下:

CPP代码

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. 组合总和 Ⅳ

力扣题目链接

文章链接:377. 组合总和 Ⅳ

视频链接:装满背包有几种方法?求排列数?| LeetCode:377.组合总和IV

状态:典型的排列问题,其实就是一个区别,就是遍历顺序的问题,只要我们先遍历背包,再遍历物品,就可以把物品进行反复选择,从而得出排列总和为target的个数

首先先明确一下什么是排列,什么是组合:

  • 组合不强调顺序,(1,5)和(5,1)是同一个组合。

  • 排列强调顺序,(1,5)和(5,1)是两个不同的排列。

我们在写回溯的时候,写过几次组合总和的问题,里面其实本质也是求排列,不过回溯是要求把所有的排列都列出来,而不是求排列总和相等的个数。

如果本题要把排列都列出来的话,只能使用回溯算法爆搜

思路

  • 确定dp数组以及下标的含义

dp[i]:凑成目标正整数为i的排列个数为dp[i]

  • 确定递推公式

dp[j](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导出来。

因为只要得到nums[j],排列个数dp[j - nums[i]],就是dp[j]的一部分。

本题还是我们经常谈论的,求装背包有几种方法,递推公式一般都是dp[j] += dp[j - nums[i]]

  • dp数组如何初始化(dp的初始化非常重要)

在求装满背包的多少种组合问题时,其实就是让dp[0]初始化为1,这样递归其他dp[i]的时候才会有数值基础

然后非零下标初始化为0

  • 确定遍历顺序

如果求组合数就是外层for循环遍历物品,内层for遍历背包

如果求排列数就是外层for遍历背包,内层for循环遍历物品

如果把遍历nums(物品)放在外循环,遍历target的作为内循环的话,举一个例子:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!

  • 举例来推导dp数组(当题目不能AC的时候一定要进行尝试)

20230310000625

CPP代码

关于递推公式前的条件判断语句:
一方面是防止下标超过索引下标;
另一方面防止整数溢出。

class Solution {
public:int combinationSum4(vector<int>& nums, int target) {vector<int> dp(target + 1, 0);dp[0] = 1;for (int i = 0; i <= target; i++) { // 遍历背包for (int j = 0; j < nums.size(); j++) { // 遍历物品if (i - nums[j] >= 0 && dp[i] < INT_MAX - dp[i - nums[j]]) {dp[i] += dp[i - nums[j]];}}}return dp[target];}
};
//直接讲元素定义成题目规定的uint
class Solution {
public:int combinationSum4(vector<int>& nums, int target) {vector<uint> 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 - nums[i]]; }}return dp[target];}
};

扩展题

还记得我们的爬楼梯吗?

70.爬楼梯

爬楼梯中,如果需要n阶才能爬到楼顶,每次你可以爬 1 或 2 个台阶。有多少种不同的方法可以爬到楼顶呢?

进一步:

如果一次可以爬3、4甚至m个台阶,一共需要爬n阶才能爬到楼顶,又如何求爬到楼顶的方法数呢?

联系到本题来看,

一步可以爬几个台阶,就相当于本题的nums=[1, 2, 3],就是一步可以爬1、2、3个台阶,target就相当于是要target阶才能爬到楼顶。

也就是装满这个背包(爬到楼顶)有多少种方法,典型的排列问题,和本题是一样一样的。

明天我们就是爬楼梯(进阶版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/7658.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ITK配准】第四期 多分辨率配准

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享ITK中多分辨率的配准,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 目录 前言 1. 使用样例…

Npm基本解说

npm&#xff08;Node Package Manager&#xff09;是Node.js的一个包管理工具&#xff0c;它允许你安装、更新、卸载和发布Node.js应用程序的依赖项。下面我将详细解释npm的一些核心功能和用法。 1. 安装依赖项 你可以使用npm install命令来安装一个或多个依赖项。例如&#…

RustGUI学习(iced)之小部件(七):如何使用图像image部件来显示图片?

前言 本专栏是学习Rust的GUI库iced的合集,将介绍iced涉及的各个小部件分别介绍,最后会汇总为一个总的程序。 iced是RustGUI中比较强大的一个,目前处于发展中(即版本可能会改变),本专栏基于版本0.12.1. 概述 这是本专栏的第七篇,主要讲述图像Image部件的使用,会结合实例…

【数据结构与算法】力扣 102. 二叉树的层序遍历

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a; root [3,9,20,null,null,15,7] 输出&#xff1a; [[3],[9,20],[15,7]]示例 2&#x…

上证50etf期权到底该怎么玩?

今天期权懂带你了解上证50etf期权到底该怎么玩&#xff1f;ETF期权是一种股票市场上的金融衍生品&#xff0c;它是在交易所上市交易的期权合约&#xff0c;其标的资产是某个特定的交易所交易基金&#xff08;ETF&#xff09;&#xff0c;如上证50指数ETF或沪深300指数ETF等。 上…

Git命令Gitee注册idea操作git超详细

文章目录 概述相关概念下载和安装常见命令远程仓库介绍与码云注册创建介绍码云注册远程仓库操作关联拉取推送克隆 在idea中使用git集成add和commit差异化比较&查看提交记录版本回退及撤销与远程仓库关联 push从远程仓库上拉取&#xff0c;克隆项目到本地创建分支切换分支将…

(✌)粤嵌—2024/5/7—除自身以外数组的乘积

代码实现&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/ int* productExceptSelf(int *nums, int numsSize, int *returnSize) {// 左乘积int l[numsSize];l[0] 1;for (int i 1; i < numsSize; i) {l[i] l[i - 1] * nums[…

为什么谷歌浏览器插件中不能使用xmlhttprequest(axios)

按照Chrome Extension官方说明&#xff0c;在MV3中&#xff0c;由于Service Workers的机制&#xff0c;background pages中不支持XMLHttpRequest&#xff0c;建议使用原生fetch()。由于axios使用的是XMLHttpRequest&#xff0c;因此&#xff0c;不能使用axios进行API请求。 在V…

什么是 Web 安全

什么是 Web 安全 培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 极安云科专注于技能提升&#xff0c;赋能 20…

Cesium学习——渲染、加载GeoJSON、调整位置

渲染概述 作者&#xff1a;当时明月在曾照彩云归 出处&#xff1a;https://www.cnblogs.com/jiujiubashiyi/p/17124717.html 1. 引言 Cesium是一款三维地球和地图可视化开源JavaScript库&#xff0c;使用WebGL来进行硬件加速图形&#xff0c;使用时不需要任何插件支持&#xf…

以中国为目标的DinodasRAT Linux后门攻击场景复现

概述 在上一篇《以中国为目标的DinodasRAT Linux后门剖析及通信解密尝试》文章中&#xff0c;笔者对DinodasRAT Linux后门的功能及通信数据包进行了简单剖析&#xff0c;实现了对DinodasRAT Linux后门心跳数据包的解密尝试。 虽然目前可对DinodasRAT Linux后门的通信数据包进…

供应商管理软件:帮助企业确定供应商的可靠性

采用单一供应商还是多家供应商&#xff0c;是企业在供应链管理领域面临的一个关键决定。 在德勤对采购专业人士进行的一项调查中&#xff0c;70% 的受访者表示&#xff0c;他们使用多家供应商来管理供应链风险。然而&#xff0c;61% 的受访者表示&#xff0c;他们也使用单一采…

SecretFlow学习指南(3)框架拆解和使用

“隐语”架构设计全貌 1.隐语框架设计思想 隐私计算是一个新兴的跨学科领域&#xff0c;涉及密码学、机器学习、数据库、硬件等多个领域。根据过去几年的实践经验&#xff0c;我们发现 ●隐私计算技术方向多样&#xff0c;不同场景下有其各自更为合适的技术解决方案 ●隐私计算…

Windows系统安装MySQL数据库详细教程

【确认本地是否安装mysql】 &#xff08;1&#xff09;按【winr】快捷键打开运行&#xff1b; &#xff08;2&#xff09;输入services.msc&#xff0c;点击【确定】&#xff1b; &#xff08;3&#xff09;在打开的服务列表中查找mysql服务&#xff0c;如果没有mysql服务&am…

麦肯锡精英高效阅读法笔记

系列文章目录 如何有效阅读一本书笔记 读懂一本书笔记 麦肯锡精英高效阅读法笔记 文章目录 系列文章目录序章 无法读书的5个理由无法读书的理由① 忙于工作&#xff0c;没时间读书无法读书的理由② 不知应该读什么无法读书的理由③ 没读完的书不断增多无法读书的理由④ 工作繁…

十进制转任意进制(以及任意进制来回转换<了解>)

十进制转任意进制&#xff1a; #include <iostream> #include <vector> #include <string> using namespace std; // 将十进制数转换为P进制形式的字符串 string toBase(int num, int base) {string result ""; // 初始化结果字符串为空wh…

《QT实用小工具·五十三》会跑走的按钮

1、概述 源码放在文章末尾 该项目实现了会逃跑的按钮&#xff1a; 两个按钮&#xff0c;一个为普通按钮&#xff0c;另一个为会跑走的按钮 鼠标移到上面时&#xff0c;立刻跑掉 针对鼠标、键盘、触屏进行优化 随机交换两个按钮的文字、偶尔钻到另一个按钮下面、鼠标移开自…

pytorch_lightning 训练教程

步骤1&#xff1a;引入必要的库 首先&#xff0c;确保你已经安装了 pytorch_lightning。pip 安装&#xff1a; pip install pytorch_lightning 然后在你的代码中导入必要的库&#xff1a; import pytorch_lightning as pl from pytorch_lightning.callbacks import ModelC…

JDBC——事务相关

JDBC中的事务提交机制 JDbC 事务机制&#xff1a; JDBC中的事务是自动提交的&#xff0c;即JDBC中DML语句执行一次&#xff0c;事务自动提交一次&#xff0c; 这是JDBC默认的事务行为&#xff0c;但是在实际的业务开发中&#xff0c;通常都是N条DML语句共同联合才能完成的&a…

2024.1.1 IntelliJ IDEA 使用记录

2024.1.1 IntelliJ IDEA 使用记录 下载设置文件编码maven 配置 插件可以中文语言包安装lombok 插件Smart Tomcat ( 根据需要安装)Smart Tomcat 配置 项目导入java 设置maven 配置 项目运行SpringBoot 项目运行tomcat 运行 (根据需要)相关依赖添加运行配置 下载 IntelliJ IDEA …