动态规划——完全背包问题(公式推导,组合、排列)

        本文章是对于完全背包 一些题型(如题目所示,组合、排列和最小值类型)的总结和理解,依次记录一下,方便回顾与复习。

        本文章是基于个人所总结 实现的,但在其中遇到了一些疑惑与困难,所以总结一篇与完全背包相关的问题。

        题型分为 完全背包 求组合问题 、 求排列问题、 求最小值问题.

但这一切都是基于完全背包,我们先来介绍一下什么是完全背包。

目录

完全背包问题

二维dp

 二维优化

一维dp(滚动数组)

完全背包组合和排列问题


完全背包问题

        有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],其价值为value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。(即如何在有限的空间中 尽可能放到整体价值最高).

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

        在这里我认为对你对01背包 已经有一定的了解,便不再深入赘述。如果不了解01背包,最好先了解之后再继续阅读。

二维dp

这是 01背包的核心代码

     // 二维数组vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));// 初始化for (int j = weight[0]; j <= bagweight; j++) {dp[0][j] = value[0];}// weight数组的大小 就是物品个数for(int i = 1; i < weight.size(); i++) { // 遍历物品for(int j = 0; j <= bagweight; j++) { // 遍历背包容量if (j < weight[i]) dp[i][j] = dp[i - 1][j];//如果当前背包容量小于物品体积,则直接不做选择else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);//如果大于物品体积,则取 选择当前物品 和 不选择当前物品 的最大值}}

01背包是每次从中选取一个物品 一次,即如果作选取决策的话,当前重量j 只用减去一个weight[i],所以选取后的重量是dp[i-1][j-weight[i]].

注意我下面所说的当前容量,而没有说背包总容量。因为我们是从小到大遍历的背包容量。

完全背包是 可以选取一个物品 多次(k>=0),即如果作选取决策的话当前重量j 要减去 k个weight[i],具体多少取决于当前背包容量的大小。所以我们for循环 k从0开始遍历,直至 大于当前背包容量停止。而我们也要做出抉择,即选出0-k次中最大的值,如下:

max (dp[i-1][j-weight[i] * k] + value[i]*k)

如果作不选取 决策的话,那么当前的值就不变化,继承上一次的值。即dp[i][j] = dp[i-1][j];

 所以现在状态转移方程为:

dp[i][j] = max { max (dp[i-1][j-weight[i] * k] + value[i]*k), dp[i-1][j] }. 其中(1 <= k <= j/weight[i]

代码如下:

    //n代表物品的数量,v代表背包的容量,weight[i]代表第i个物品的体积,value[i]是第i个物品的价值vector<vector<int>> dp(N+1,vector<int>(N+1,0));//遍历背包物品for(int i = 1; i <= n; i++){  //遍历背包容量for(int j = 1; j <= v; j++){//每次选取k件 物品i ,如果容量大于 当前容量j,则停止for(int k = 1; k * weight[i] <= j; k++){//这句代码相当于max (dp[i-1][j-weight[i] * k] + value[i]*k),而由于k每次在同一行,所以每次和dp[i][j]进行比较。当然这里写max(dp[i-1],...)也没关系,因为最后都是取的 dp[i-1][j-k*weight[i]]+value[i]*k)的最大值。选dp[i-1][j]相当于每次都和第一次比较,而选dp[i][j]相当于每次都和上一次的进行比较,所以dp[i][j]最后一定是最大值dp[i][j] = max(dp[i][j],dp[i-1][j-k*weight[i]]+value[i]*k);}//dp[i][j] = max(dp[i-1][j],dp[i][j]); //这句代码其实不用加,因为上面第一次k一定等于0,//所以相当于第一次已经比较了。这里写只是为了更好的显示上面的思路}}return dp[n-1][v];

 二维优化

上面我们用了三重for循环,时间复杂度太高了,我们有没有办法把它转化成二维的呢?

记得上面刚说的这个状态转移方程:

一.dp[i][j] = max { max (dp[i-1][j-weight[i] * k] + value[i]*k), dp[i-1][j] }.

其中(1 <= k <= j/weight[i])

我们看到k的取值最小值为1,那我们不妨先把这 一个物品放进去

得到:

二.dp[i][j] = max {max(dp[i-1][j-weight[i]*k -weight[i]]+value[i]*k +value[i]),dp[i-1][j]);

此时k的取值范围为:(0 <= k <= j/weight[i]-1)

对于式子一,我们完全可以可以把式子简化为如下:

三.dp[i][j] = max(dp[i-1][j-weight[i] * k] + value[i]*k),其中

其中(0 <= k <= j/weight[i])

这是如何做到的呢?我们发现式子一 k的最小值是1,那么当我们让k=0时,发现dp[i-1][j-weight[i] * k] + value[i]*k)就是dp[i-1][j].所以我们完全可以合并这两个!最后只留下一个max,只不过k的最小值由1变成了0.

j=j-weight[i]时,我们将其带入到式子三:

四.dp[i][j-weight[i]] = max(dp[i-1][j-weight[i] - weight[i]*k] + value[i]*k);其中:

(0 <= k <= j/weight[i]-1)

此时我们再用式子二对比式子四:

二.dp[i][j] = max {max(dp[i-1][j-weight[i]*k -weight[i]]+value[i]*k +value[i]),dp[i-1][j]);

四.dp[i][j-weight[i]] = max(dp[i-1][j- weight[i]*k -weight[i]] + value[i]*k);

 可以发现它们是画圈的这一部分完全等价的!范围也是一样的。我们我们把式子四 替换到式子二中:

dp[i][j] = max(dp[i][j-weight[i]]+value[i],dp[i-1][j]).

这就是我们最终推导出的公式!!!

所以我们再也不需要写那三重循环了,直接二维循环就可以,如下:

    vector<vector<int>> dp(N+1,vector<int>(N+1,0));//遍历背包物品for(int i = 1; i <= n; i++){  //遍历背包容量for(int j = 0; j <= v; j++){//如果选择的话if(j >= weight[i])dp[i][j] = max(dp[i][j-weight[i]]+value[i],dp[i-1][j]);//如果不选择elsedp[i][j] = dp[i-1][j];}}

一维dp(滚动数组)

和01背包问题问题类似,我们同样可以转化为把  二维数组转化为一维数组,因为它们都只依赖于上一行的状态。

因此我们由

dp[i][j] = max(dp[i][j-weight[i]]+value[i],dp[i-1][j]);

可以转化为一维dp数组:

dp[j] = max(dp[j-weight[i]]+value[i],dp[j]);

修改完毕后,代码如下:

    vector<int> dp(N+1,0);for(int i = 1; i <= n; i++){for(int j = weight[i]; j <= v; j++){dp[j] = max(dp[j-weight[i]]+value[i],dp[j]);}}return dp[v];

完全背包组合和排列问题

先说结论:

利用背包 求组合数外层遍历物品,内层遍历 物品容量

利用背包求 排列数 是 想外层遍历物品的容量内层再遍历 物品.

组合 不强调顺序,而排列强调顺序

为什么是这样呢?

假设你有价值为1元 和 2元的硬币,数量分别有无限个,那么让你凑成价值为5元的方案有多少种?

这个里面,有这样的两种方案:1 2 2    2  1 2  

如果是组合数,它们将会被视为一种组合,而如果是排列数,那么它们将是两种不同的排列

具体来说:

假设你先遍历的物品数量,那么你后面只能按照固定的顺序遍历了。

假设物品有 abc,那么你也只能按照abc这个顺序来放入了. b根本没机会放到a的前面,因为遍历完a结束 才遍历b的. 所以这样是求组合数

相反的,假设你先遍历的物品容量,再遍历物品数量,这样每个物品都有机会放入背包中,顺序也不固定,假设装a不合适,我可以装b(ee,真没别的意思),随着遍历背包容量变大,说不定原来的a又适合装入了,这样就有了不同的顺序了。

大家可以在leetcode上做下面的例题感受:

求组合数:
518.零钱兑换II

求排列数:

377.组合总和IV

到这里,关于完全背包的一些基础相关的问题就讲完了,实际上还需要大家看视频或者做更多题感受到,可以看看题解中一些人发表的,他们发表的一种种类型及对应的题目可以非常好的巩固自己!

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

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

相关文章

Spring基于注解开发

Component的使用 基本Bean注解&#xff0c;主要是使用注解的方式替代原有的xml的<bean>标签及其标签属性的配置&#xff0c;使用Component注解替代<bean>标签中的id以及class属性&#xff0c;而对于是否延迟加载或是Bean的作用域&#xff0c;则是其他注解 xml配置…

四招打造完美分层自动化测试框架,让测试更高效!

写在前面 我们刚开始做自动化测试&#xff0c;可能写的代码都是基于原生写的代码&#xff0c;看起来特别不美观&#xff0c;而且感觉特别生硬。 来看下面一段代码&#xff1a; 具体表现如下&#xff1a; driver对象在测试类中显示 定位元素的value值在测试类中显示 定位元素…

Navicat 技术指引 | 适用于 GaussDB 分布式的用户/权限功能

Navicat Premium&#xff08;16.3.3 Windows 版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB 分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结…

干货:软文推广中的关键词类别有哪些?

软文推广如果想要增加文案曝光率&#xff0c;seo是其主要的传播方式之一&#xff0c;因而好的关键词十分重要&#xff0c;这里的关键词指得是针对搜索引擎而言&#xff0c;由用户输入搜索引擎框中的提示性文字&#xff0c;只要关键词设置得好&#xff0c;软文就能通过搜索引擎精…

因为 postman环境变量全局变量设置好兄弟被公司优化了!

postman环境变量、全局变量设置 在公司中&#xff0c;一般会存在开发环境、测试环境、线上环境等&#xff0c;如果需要在不 同的环境下切换做接口测试&#xff0c;显然我们需要把所有接口的域名进行修改&#xff0c;如果接 口测试用例较多&#xff0c;那么修改会非常费力&…

Python与ArcGIS系列(十五)根据距离抓取字段

目录 0 简述1 实例需求2 arcpy开发脚本0 简述 在处理gis数据的时候,会遇到这种需求:将一个图层与另一个图层中相近的要素进行字段赋值。本篇将介绍如何利用arcpy及arcgis的工具箱实现这个功能。 1 实例需求 为了介绍这个功能的实现,我们需要有一个特定的功能需求。在这里选…

L1-019:谁先倒

题目描述 划拳是古老中国酒文化的一个有趣的组成部分。酒桌上两人划拳的方法为&#xff1a;每人口中喊出一个数字&#xff0c;同时用手比划出一个数字。如果谁比划出的数字正好等于两人喊出的数字之和&#xff0c;谁就输了&#xff0c;输家罚一杯酒。两人同赢或两人同输则继续下…

Axure网页端高复用组件库, 下拉菜单文件上传穿梭框日期城市选择器

作品说明 组件数量&#xff1a;共 11 套 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;web端原型设计、桌面端原型设计 作品特色 本作品为「web端组件库」&#xff0c;高保真高交互 (带仿真功能效果)&#xff1b;运用了动态面板、中继…

使用pytorch查看中间层特征矩阵以及卷积核参数

这篇是我对哔哩哔哩up主 霹雳吧啦Wz 的视频的文字版学习笔记 感谢他对知识的分享 1和4是之前讲过的alexnet和resnet模型 2是分析中间层特征矩阵的脚本 3是查看卷积核参数的脚本 1设置预处理方法 和图像训练的时候用的预处理方法保持一致 2实例化模型 3载入之前的模型参数 4载入…

Pandas操作数据库

一&#xff1a;Pandas读取数据库数据 二&#xff1a;Pandas读取海量数据 三&#xff1a;Pandas向数据库存数据 四&#xff1a;Pandas写入海量数据

上海宝山区12月8日发生一起火灾 火势已扑灭 揭秘AI如何“救援”

在这个冬日的早晨&#xff0c;上海宝山区的居民经历了一场惊心动魄的火灾。幸运的是&#xff0c;火势很快就被扑灭了。但这起事件不禁让我们思考&#xff1a;如何更有效地预防和应对这样的紧急情况&#xff1f; 这时候&#xff0c;就不得不提到北京富维图像公司的一项创新技术—…

我的隐私计算学习——国密SM2和国密SM4算法

此篇是我笔记目录里的安全保护技术&#xff08;七&#xff09;&#xff0c;前篇可见&#xff1a; 隐私计算安全保护技术&#xff08;一&#xff09;&#xff1a;我的隐私计算学习——混淆电路-CSDN博客 隐私计算安全保护技术&#xff08;二&#xff09;&#xff1a;我的隐私计…

当下流行视频剪辑软件会声会影2024,让你的视频制作更精彩

大家好呀&#xff01;今天小编给大家介绍一款超赞的视频编辑软件——会声会影2024&#xff01; 当下流行视频剪辑软件会声会影2024&#xff0c;让你的视频制作更精彩&#xff0c;会声会影2024不仅提供了各种酷炫的特效和滤镜&#xff0c;还有更多令人惊叹的功能等待着你的发掘…

【STM32】蓝牙氛围灯

Docs 一、项目搭建和开发流程 一、项目需求和产品定义 1.需求梳理和产品定义 一般由甲方公司提出&#xff0c;或由本公司市场部提出 需求的重点是&#xff1a;这个产品究竟应该做成什么样&#xff1f;有哪些功能&#xff1f;具体要求和参数怎样&#xff1f;此外还要考虑售价…

[Python从零到壹] 七十三.图像识别及经典案例篇之图像去雾ACE算法和暗通道先验去雾算法实现

十月太忙&#xff0c;还是写一篇吧&#xff01;祝大家1024节日快乐O(∩_∩)O 欢迎大家来到“Python从零到壹”&#xff0c;在这里我将分享约200篇Python系列文章&#xff0c;带大家一起去学习和玩耍&#xff0c;看看Python这个有趣的世界。所有文章都将结合案例、代码和作者的经…

更多内窥镜维修技能学习与交流可关注西安彩虹

内窥镜结构及光学成像原理 众多品牌的硬镜其内部结构基本相似&#xff08;如下图&#xff09;&#xff0c;最关键的在于不同用途的硬镜在其结构上发生变化&#xff0c;包括光学成像系统和机械结构。光学成像系统由物镜系统、转像系统、目镜系统三大系统组成。 工作原理 被观察…

MySQL中是如何insert数据的

正常insert数据&#xff0c;MySQL并不会显式加锁&#xff0c;而是通过聚簇索引的trx_id索引作为隐式锁来保护记录的。比如两个事务对一个非唯一的索引情况添加&#xff0c;会造成幻读 但在某些特殊情况下&#xff0c;隐式锁会转变为显式锁&#xff1a; 记录之间有间隙锁inser…

二叉树的非递归遍历(详解)

二叉树非递归遍历原理 使用先序遍历的方式完成该二叉树的非递归遍历 通过添加现有项目的方式将原来编写好的栈文件导入项目中 目前项目存在三个文件一个头文件&#xff0c;两个cpp文件&#xff1a; 项目头文件的代码截图&#xff1a;QueueStorage.h 项目头文件的代码&#xff…

如何运用gpt改写出高质量的文章 (1)

大家好&#xff0c;今天来聊聊如何运用gpt改写出高质量的文章 (1)&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 如何运用GPT改写出高质量的文章 一、引言 随着人工智能技术的飞速发展&#xff0c;自然…

字符串指令集

字符串指令的格式 例子1就成功发送了指令 例子2就是发送的字符串有误 查询当前位置就会在附加信息中返回当前座位的坐标 第一个指令的参数就是闪灯的两个参数 如第一个示例就是10ms On Time 第二个就是Off Time 使用标准库来接收字符串命令 字符串指令的接收 因为一个指令就是…