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

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

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

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

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

目录

完全背包问题

二维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.hqwc.cn/news/258531.html

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

相关文章

spring 的概述和入门

​ 我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; …

Python语言求解嵌套列表中的最大元素和

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在处理嵌套列表时&#xff0c;有时我们需要找到列表中的最大元素以及对应的位置。本文将深入讨论如何使用Python有效地解决这个问题。我们将使用不同的方法&#xff0c;包括递归、列表推导和NumPy库&#xff0c;…

微信小程序基础

1.小程序发展史 微信小程序之前&#xff0c;是使用weixin-sdk进行开发&#xff0c;调用视频&#xff0c;摄像头等。 微信小程序weixin up端&#xff0c;所以PC端的window这些没有&#xff0c;运行环境是IOS&#xff0c;安卓等&#xff0c;有一些特殊的调用录音功能&#xff0…

【以医院为案例】讲如何画业务架构图

【以医院为案例】讲如何画业务架构图 背景知识 什么是业务&#xff1f; 业务是个人或企业为了获利而进行的有组织的努力和活动。 以医院为案例: 业务是指医院提供医疗服务的活动。患者通过消费来享受医院提供的医疗服务&#xff0c;从而重新获得健康的身体。可以将患者去医院…

实战| 通杀漏洞挖掘技巧

前言 前端时间&#xff0c;要开放一个端口&#xff0c;让我进行一次安全检测&#xff0c;发现的一个漏洞。 经过 访问之后发现是类似一个目录索引的端口。(这里上厚码了哈) 错误案例测试 乱输内容asdasffda之后看了一眼Burp的抓包&#xff0c;抓到的内容是可以发现这是一个…

家用洗地机希亦、追觅和添可哪款好用?测评PK谁是清洁之王

对于上班族来说&#xff0c;时间非常宝贵&#xff0c;打扫卫生就成为了一件比较痛苦的事情。现在的都市上班族都会寄托于智能家电。在当前市场上&#xff0c;洗地机已成为家庭清洁的面部工具。洗地机是一种高效的清洁设备&#xff0c;以其自动化、高效率的清洁功能&#xff0c;…

express搭建后台node接口

在前端的学习中我们使用express来开发接口结合mysql&#xff0c;然后使用可视化的数据库工具来操作数据&#xff0c; web框架是express 文档是jsdoc swagger 数据库模型是sequelize 部署使用PM2来上服务器&#xff0c; 打包你也可以结合webpack配置target node状态 当然你也可以…

ES-环境安装(elasticsearch:7.17.9,kibana,elasticsearch-head)

ES 环境搭建 1 拉取镜像 常用三件套 docker pull kibana:7.17.9 docker pull elasticsearch:7.17.9 docker pull mobz/elasticsearch-head:52 启动镜像 elasticsearch 安装 这里可以先不挂载文件启动一波&#xff0c;然后把容器里的文件拷贝出来 docker run -p 19200:9200 …

【AI】以大厂PaaS为例,看人工智能技术方案服务能力的方向(2/2)

目录 三、解决方案 3.1 人脸身份验证 3.2 图像审核&#xff08;暴恐、色情等&#xff09; 3.3 人脸会场签到 3.4 机器人视觉 3.5 视频审核 3.6 电商图文详情生成 3.7 智能客服 接上回&#xff1a; 【AI】以大厂PaaS为例&#xff0c;看人工智能技术方案服务能力的方向&…

SSL 协议

SSL 是用于安全传输数据的一种通信协议。它采用公钥加密技术、对称密钥加密技术等保护两个应用之间的信息传输的机密性和完整性。但是&#xff0c;SSL 也有一个不足&#xff0c;就是它本身不能保证传输信息的不可否认性。 SSL 协议包括服务器认证、客户认证、SSL 链路上的数据完…

MySQL - 表达式With as 语句的使用及练习

目录 8.1 WITH AS 的含义 8.2 WITH AS语法的基本结构如下&#xff1a; 8.3 练习题1 8.4 牛客练习题 8.1 WITH AS 的含义 WITH AS 语法是MySQL中的一种临时结果集&#xff0c;它可以在SELECT、INSERT、UPDATE或DELETE语句中使用。通过使用WITH AS语句&#xff0c;可以将一个查…

Python os模块及用法

os 模块代表了程序所在的操作系统&#xff0c;主要用于获取程序运行所在操作系统的相关信息。 在 Python 的交互式解释器中先导入 os 模块&#xff0c;然后输入 os.__all__ 命令&#xff08;__all__ 变量代表了该模块开放的公开接口&#xff09;&#xff0c;即可看到该模块所包…