算法--动态规划(背包问题)

这里写目录标题

  • 总览
  • dp问题的优化
  • 01背包问题
    • 概述
    • 算法思想
    • 算法思想中的注意点
    • 例题+代码
    • 等效为一维数组
  • 完全背包问题
    • 概述
    • 算法思想
    • 例题+代码
    • 等效为二维数组
    • 等效为一维数组
  • 多重背包问题
    • 概述
    • 算法思想
    • 例题+代码
    • 优化(采用二进制的方式)
      • 基本思想
      • 时间复杂度
      • 例题+代码
  • 分组背包问题
    • 概述
    • 算法思想

总览

在这里插入图片描述

dp问题的优化

在这里插入图片描述
要清楚:dp问题的优化一般是对dp问题的代码或者计算方程做一个等效变形
有了这个前提,我们在写dp问题时,要先将基本的代码写出来,之后再做优化

01背包问题

概述

在这里插入图片描述
假设我们有N个物品,我们的背包的体积是V,
N个物品每个物品有两个属性,分别是v体积、和w价值,或者说权重,每个物品要么不选,如果选的话,只能选一次
我们的目标是:要选出一些物品,在总体积能装的下的情况下(不一定必须装满),争取价值之和最大化

算法思想

在这里插入图片描述
Dp问题,要考虑两个问题,一个是状态表示,一个是状态计算
对于01背包问题,
状态表示:
我们先来看状态表示,因为大前提是N个物品和V的体积,也就是不算物品属性的话,我们有两个参数,所以,状态表示f,就有两个参数,那他就是二维的状态表示,f(i,j)
而对于一个状态表示 f,我们要清楚,他是一个集合,那么一个集合就会有属性,一个集合有三种属性,根据不同的题设,选择不同的属性,三种属性分别是max(元素最大值)、min(元素最小值)、数量(元素数量),本题根据题设,是属性选定为max,表示求最大价值
那这个集合的元素表示什么呢,该集合的元素表示在所有的选法中,只从前 i 个物品选择,总体积小于等于 j 的选法
总结:状态表示就是将题意数学化,将题设信息数学化为 f(i,j)
状态计算:
状态计算就是对上面的 f(i,j)进行计算,主要用到集合划分的思想
首先将集合划分为两部分,
第一个部分是 f 集合中所有不含 i 的选法的最大值,那么就是从1~i-1中选,总体积不超过 j ,也及时 f(i-1,j)
第二个部分是 f 集合中所有含 i 的选法的最大值,那么我们先将 i 排除,求得不算 i 时剩余的值最大的选法,因为排除了一个 i ,那么体积限制也跟着缩小,所以是从1~i-1中选,总体积不超过 j-vi 的选法的元素的最大值,即 f (i-1,j-vi)

最后,将两部分取maxAPI,求得 f(i,j),注意,因为第二部分是将 i 排除之后计算得出的,所以,在取max时,第二个参数要加上i的价值wi,即第二个参数为 f(i-1,j-vi)+ wi
如下三张图
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

算法思想中的注意点

首先,f(i,j)表示在前i个物品中选,总体积不超过 j 的选法,最大价值的值
这里的i 和 j 是通量,相当于将题设扩展成普遍性了,不是题设的最终量,也就是 i 和 j在代码中是变化的,最后是要输出f(N,V)

其次,对于状态计算,不含i的部分一定会出现,含i部分未必一定会出现,所以代码中要加一个判断,代码中会体现,注意留意

再者,代码的循环存在从0到等于终点,而不是0到小于终点或者1到等于终点,这点要注意,只不过有些循环的0初始值就是0,所以循环从1开始到等于终点,具体循环的范围,要根据代码原理、功能具体分析,不要想当然,要实事求是

例题+代码

在这里插入图片描述
在这里插入图片描述
n,m分别表示有n个物品,总体积是m
v[N],w[N]数组,分别存储每个物品的体积和价值
f[N][N],是状态表示,也就是前i个物品中选,总体积不超过j的选法中,价值最大的值

main函数里,
首先输入n和m
然后for循环,依次向v,和w中输入值(注意,i从1开始到等于n,因为这里是赋值下标物品的数据,没有第0个物品,所以i从1开始到等于n)
之后,双重循环 i 从1到等于n,j从0到等于m(至于为什么i从1开始而不从0开始,是因为f[0][0~m],表示前0个物品中,选出体积不超过 j的选法,因为物品是0,所以总价值也是0,所以f[0][0到m]都是0,又因为int定义自动初始化为0,所以不用管 i =0的情况)
循环内,f[i][j] = f[i-1][j],先将这个赋值给f[i][j]
之后判断第二部分,因为第二部分只有在 j>=v[i]时,才会出现,所以加上if判断之后,f[i][j] = max( f[i][j] , f[i-1][ j-v[i] ] + w[i] )
(因为第一步直接将值赋给了f[i][j],所以这里是用f[i][j]进行对比,这样做的好处是,将第一部分与第二部分隔离开,因为第二部分需要特判)

等效为一维数组

在这里插入图片描述
首先我们将 i 维直接去掉,接下来代码中的 i 维也跟着去掉,这样的话,双重循环中的第一行代码就变成了上图所示,由于f[ j ] = f[ j ] 是恒等式,所以可以直接去掉
在这里插入图片描述
之后,由于此时仅剩第二条代码,所以可以对 j 的循环范围进行更改,将if去掉,因为只有 j >= v[i] 才会执行第二条语句,所以,直接将j的循环范围改成 j=v[i] ; j <= m ; j++,然后将if去掉
在这里插入图片描述
然后直接将这行代码的 i 维去掉,就变成了上图所示,但是直接去掉是不合法的,应该进行一些其他变动
原理:(选择性查看)但是这样的话,从一维数组考虑,由于f[ j - v[i] ] 中的 j - v[i] 肯定小于 f[ j ] 中的 j ,而 j 是从小到大递增的,所以可以知道,f[ j - v[i] ] 在 f[ j ]被计算之前就被计算了,所以当前他已经有结果了,可以放在当前层,所以等效为上图箭头所示,而我们原来的代码中,第二个参数应该是f[i-1][j-v[i]+w[i]],所以,直接去掉是不合法的,应该进行一些其他变动
在这里插入图片描述
我们可以将j的范围掉头,让其从大到小进行遍历,就合法了
原理:(选择性查看)这样的话,从一维的角度来看,f[ j - v[i]] 在 f[j] 被计算之后才会被进行计算,也就不会在i层,而是等待被计算,又因为j-v[i]小,i从小到大遍历,所以这时等效为上图所示,f[i-1] [j - v[i] ] ,这时,就完成了等效
在这里插入图片描述
最终的代码等效为了上图,这就是01背包代码的终极形态

完全背包问题

概述

在这里插入图片描述
完全背包问题,是每个物品有无限个,每个物品都可以选无限次

算法思想

在这里插入图片描述
仍然是那几步,只不过这次前 i 个物品可以用无限次
主要区别在于状态计算,这里的划分依据是,对于第i个物品,选了几次
如果是选了0次,那么就是 f[ i-1 , j ]
如果选了k次,那么还是根据曲线救国的思想,先将k个i去掉,剩余部分求最大值,最后加回来k个i的价值
即 f[i-1 , j-kv[i] ] + kw[i]
(之所以还是 i -1 ,是因为 f 的含义是前i个物品,总体积不超过 j ,并没有强调前i个物品的次数,现在只是去掉了一个物品 i ,并没有涉及到 i 的次数)

例题+代码

在这里插入图片描述
在这里插入图片描述
这里又加了第三重循环,是加入k,表示物品 i 用了k次,因为又总体积和物品 i 的单位体积存在,所以k是有限的,k初始化为0,条件为 k * v[i] <= j
之后代码中直接用f[i][j] , 和 f[i - 1][j - k *v[i]] + k *w[i],进行max,这里之所以没有01背包中的两行代码是因为这里的划分,统一用的是一个公式,仅一个公式就包括了所有的划分情况(即 f[i - 1][j - k *v[i]] + k *w[i]这个公式),所以,直接一行代码即可,没有其他特殊情况

等效为二维数组

在这里插入图片描述
根据公式,当k从0开始取不同值的时候,我们可以得到第一行式子
假如我们求一下 f [i,j-v] ,k也从0开始取不同值,我们可以得到第二行式子,发现第二行式子加上w,就是第一行式子中,k!=0的结果,所以可以根据这个关系,将k的for循环删去,等效为双重循环
所以,等效之后的代码与01背包的朴素算法长的极为相似,唯一不同的是,在if后面的代码,这里是 f[i][j-v[i]]+w[i],而前面是f[i-1][j-v[i]]+w[i]
在这里插入图片描述
在这里插入图片描述

等效为一维数组

在这里插入图片描述
由于上面已经优化成二维数组了,且和01背包的代码仅仅差别在max的第二个参数中 f 数组的第一个参数,完全背包是 f[i][j-v[i]]-w[i],而01背包是 f[i-1][j-v[i]]+w[i]
完全背包的代码正好符合01背包向一维数组转换时,等效完 for循环中 j 的范围之后的代码,也就是完全背包问题,在转为一维时,还是跟01极为相似,不同的是,完全背包问题不用将 j 改为从大向小递减,其他均一致

多重背包问题

概述

在这里插入图片描述
多重背包问题,是每个物品的个数不一样,也就是每个物品的可选次数不一样

算法思想

在这里插入图片描述
状态表示仍然是那个,不同的是状态计算
而状态计算与上一个:完全背包问题极为相似,上面完全背包问题是每种物品都有无限次可以选,唯一的限制是体积不能超过总体积,
而这个多重背包问题,因为每组可选的次数为s[i],不再是无限次,所以,这里的状态计算是:第i个物品选了k次,k从0到s[i]
所以,代码唯一与完全背包问题不同的是,k的限制条件+1:k <= s[i];且多了一个s[ ]数组,在输入的时候,将物品i能选几次输入到数组s[i]中

例题+代码

在这里插入图片描述
在这里插入图片描述

优化(采用二进制的方式)

基本思想

在这里插入图片描述
原来的代码中,对于每个物品i,k都是从0开始递增循环,现在我们可以优化这个k的循环次数,让他不再一次一次循环
假如说,目前物品 i 的s[i] 是200,那么按照常规理解,k就要从0一直循环到200,但是,根据进制数的启发,对于一个数(例如200),他可以表示成十进制,也可以表示成二进制,如上图所示,0~127,都可以用2的0次方 到 2的6次方 加起来得到,因为一个二进制转化为十进制就是这样转化的:由几个二进制的基础数加起来得到,但是因为642=128,1282-1已经超过了200,所以128不能使用,最后差多少就补上这个差值,这样,我们就可以用几个数表示出来0到200的所有数了

而有了这个思想,我们可以将这些次数与当前物品的v和w进行结合,也就是将其打包成一个物品,一个物品有s次,可以被打包成好几个大物品,将所有的物品进行打包完之后,就可以转化成01背包问题了,这样即符合v和w,而且次数也对应可以凑出来,直接拿着打包好的这些物品,进行01背包的算法即可

时间复杂度

在这里插入图片描述
通过这样的优化,时间复杂度由NVS,优化到了NVlogS,其中logS是以2为底的对数的缩写
这时候带入数据量,如果算出来数据量最终结果为1e7~1e8,那么就可以在1s内算出来

例题+代码

在这里插入图片描述
使用该二进制进行数据打包时,特别注意的是,N开到了25000(如下图),而原始数据是1000,因为这里的1000仅仅是小物品的种类数,而每个物品有s个,但是我们进行打包时,一个物品 有s个 ,一个物品可以打包成logS个,所以就是1000*logS,带入S=2000,再加上空余量,大概就是25000
在这里插入图片描述
这里都是在打包物品,(这里将视野放在一个物品的打包即可,其他的交给for循环)
首先输入n和m
之后定义一个cnt=0,用来给打包好的物品编号(后续称为大物品)
for循环从1到等于n
定义 a b s 用来接收每个基础物品(后续称为小物品)的体积价值和次数
之后 定义k=1(用来记录2的进制数)
while循环,k <= s(为什么是k <= s,我们可以看到第27、28行,首先对于当前这个小物品,每次while循环都会将s进行更新,也就是每次s表示的都是减去目前最大的二进制之后,剩余的次数,而如果2的k次方已经是极限,则剩余的部分肯定不够2的k+1次方,所以,就有k <= s )
之后cnt++;(编号++)
v[cnt] = a * k
w[cnt] = b * k
这里就是将s个小物品打包成大物品的过程
之后s -= k
k *= 2
while循环结束后,有一个if判断,判断当s > 0 时,表示有剩余,最后将剩余次数进行打包即可
cnt++
v[cnt] = a * s;
w[cnt] = b * s;
最后将n更新为cnt,表示目前有cnt个大物品
在这里插入图片描述
最后将这些大物品进行一遍01背包的代码即可

分组背包问题

概述

在这里插入图片描述
分组背包问题,是会将物品进行分组,每一组最多只能选择改组内的一个物品,要么这个组不选,如果想选择这个组里的物品,只能选择改组内的一个物品且一次

算法思想

在这里插入图片描述
注意 这里的状态表示,从前i个变成了前i组,
状态计算,划分的时候,划分依据是,选择第i组的第k个,k可以为0(表示不选第 i 组)
如果k=0,那么就是第 i 组不选,那么就是f[i-1 , j ]
其他的 如果选第 i 组的第 k 个,那么就是 f [ i-1 , j - v[i, k]] + w[i,k]
与01背包差不多,只不过这里的 i 表示组,组中一旦有某个物品选了,那么该组就不可以第二次被选了,v、w用二维数组精确到组内成员
在这里插入图片描述
在这里插入图片描述
首先,s[N]是用来存储每一组有多少个种类的
输入n、m
之后for循环i从1到等于n,输入s[i]
且二重循环for,j从0到小于s[i],输入每个物品的v、w,因为i表示组,所以是w[i][j]、v[i][j]
(注意,因为这里对v和w赋值时用到的 j 是从0开始到小于s[i],那么之后再使用v和w时,那个循环变量也要从0到小于s[i])
其次,开始01背包一维代码的套用,
首先一个for,1从1到小于等于n
之后for,j从大到小,j从m到大于等于0,j–(其实这里的j>=0,本来对应的是 j >= v[i][k],但是k在第三重循环,所以这里改为0,将条件加到三重循环里面)
在之后三重循环for,k从0到小于s[i]
if ( j >= v[i][k]) f[ j ] = max (f[j] , f[j-v[i][k]]+w[i][k]);
最后输出 f[m]即可

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

开发Chrome插件,background.js中log打印未出现在控制台

不同于内容脚本&#xff08;通常命名content.js&#xff09;&#xff0c;在后台脚本&#xff08;通常命名background.js或service-worker.js&#xff09;中console.log并不会在控制台中直接显示。 要查看后台脚本上下文的正确控制台&#xff0c;执行如下步骤&#xff1a; 访问…

2024年2月17日~2月23日周报

文章目录 一、前言二、DDNet架构学习2.1 数据预处理2.2 网络模型构建 三、基于深度学习地震数据去噪处理3.1 深度学习在地震数据去噪中的研究方向3.2 深度学习地震数据去噪流程3.2.1 数据集准备3.2.2 模型构建3.2.3 训练网络 3.3 基于DnCNN的地震数据去噪实验 四、小结4.1 存在…

前端项目打包体积分析与优化

一、安装依赖分析工具 npm install webpack-bundle-analyz 二、修改webpack.config.js文件 1、导入上面下载的包 2、在plugins里创建实例 三、启动打包命令 npm run build 会弹出如下界面&#xff1a; 四、优化 1、通过CDN导入react-dom文件 修改webpack.config.js文件里…

【前端素材】推荐优质后台管理系统GramOs平台模板(附源码)

一、需求分析 后台管理系统是一种用于管理网站、应用程序或系统的工具&#xff0c;它通常作为一个独立的后台界面存在&#xff0c;供管理员或特定用户使用。下面详细分析后台管理系统的定义和功能&#xff1a; 1. 定义 后台管理系统是一个用于管理和控制网站、应用程序或系统…

Linux环境非root用户配置SSH免密登录,并解决登录仍提示输入密码

Linux环境非root用户配置SSH免密登录&#xff0c;并解决登录仍提示输入密码 ssh免密登录的简单理解 以A和B进行举例&#xff1a;A免密登录B &#xff08;即在A服务器输入命令&#xff1a;ssh 非root用户名B的IP地址&#xff09;可以直接免密码直接登录 A生成私钥和公钥&#…

QT基本组件

四、基本组件 Designer 设计师&#xff08;重点&#xff09; Qt包含了一个Designer程序&#xff0c;用于通过可视化界面设计开发界面&#xff0c;保存文件格式为.ui&#xff08;界面文件&#xff09;。界面文件内部使用xml语法的标签式语言。 在Qt Creator中创建文件时&#xf…

​分享87个Html企业模板,总有一款适合您

分享87个Html企业模板&#xff0c;总有一款适合您 87个Html企业模板下载链接&#xff1a;https://pan.baidu.com/s/1iBpfaBRgMJBv4pj07rZhOg?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集…

【STM32】Keil RTE使用记录

0 前言 最近因为任务需要&#xff0c;再次开始研究STM32&#xff0c;打算过一遍之前记录的笔记&#xff0c;在创建工程模板时&#xff0c;突然发现一个之前被自己忽略的东西&#xff0c;那就是创建项目时会弹出的Run-Time Environment&#xff0c;抱着好奇的心态去找了一些资料…

Rust: reqwest库示例

一、异步处理单任务 1、cargo.toml [dependencies] tokio { version "1.0.0", features ["full", "tracing"] } tokio-util { version "0.7.0", features ["full"] } tokio-stream { version "0.1" }…

【学习笔记】数据结构与算法03:栈与队列

知识出处&#xff1a;Hello算法&#xff1a;https://www.hello-algo.com/. 文章目录 2.2 栈和队列2.2.1 「栈 stack」2.2.1.1 栈的常用操作2.2.1.2 栈的典型应用 2.2.2「队列 queue」2.2.2.1 队列的常用操作2.2.2.2 队列的典型应用 2.2.3 双向队列 「double-ended queue」2.2.3…

论文阅读——SimpleClick

SimpleClick: Interactive Image Segmentation with Simple Vision Transformers 模型直接在VIT上增加交互是分割 用VIT MAE方法训练的预训练权重 用交互式分割方法微调&#xff0c;微调流程&#xff1a; 1、在当前分割自动模拟点击&#xff0c;没有人为提供的点击 受到RITM启发…

如何使用Douglas-042为威胁搜索和事件应急响应提速

关于Douglas-042 Douglas-042是一款功能强大的PowerShell脚本&#xff0c;该脚本可以提升数据分类的速度&#xff0c;并辅助广大研究人员迅速从取证数据中筛选和提取出关键数据。 该工具能够搜索和识别Windows生态系统中潜在的安全漏洞&#xff0c;Douglas-042会将注意力放在…

Jmeter基础(2) 目录介绍

目录 Jmeter目录介绍bin目录docsextrasliblicensesprintable_docs Jmeter目录介绍 在学习Jmeter之前&#xff0c;需要先对工具的目录有些了解&#xff0c;也会方便后续的学习 bin目录 examplesCSV目录中有CSV样例jmeter.batwindow 启动文件jmeter.shMac/linux的启动文件jmete…

学习负载均衡的算法

什么负载均衡 负载均衡是一种计算机技术&#xff0c;用于在多个系统、网络链接、硬盘驱动器、CPU等之间分配工作负载&#xff0c;以优化资源使用、最大化吞吐量、最小化响应时间、并避免任何单一资源的过载。在网络负载均衡的情况下&#xff0c;它可以帮助将网络流量有效地分配…

300分钟吃透分布式缓存-11讲:MC如何淘汰冷key和失效key?

淘汰策略 Mc 作为缓存组件&#xff0c;意味着 Mc 中只能存储访问最频繁的热数据&#xff0c;一旦存入数据超过内存限制&#xff0c;就需要对 Mc 中的冷 key 进行淘汰工作。Mc 中的 key 基本都会有过期时间&#xff0c;在 key 过期后&#xff0c;出于性能考虑&#xff0c;Mc 并…

SpringBoot中Redis缓存的使用

目录 1 前言 2 实现方法 2.1 查询数据时 2.2 修改数据 1 前言 对于一些不常改变&#xff0c;但又经常查询的数据&#xff0c;我们可以使用Redis缓存&#xff0c;来缓解数据库的压力&#xff0c;其中的逻辑如下&#xff1a; 2 实现方法 2.1 查询数据时 一般在控制类查询方…

SMIC思洣客矩阵:重塑数字经济的未来

经济的发展 经济是供需满足的过程&#xff0c;它涵盖了社会物资的生产和再生产过程。在这个过程中&#xff0c;经济活动遵循着生产和再生产的规律&#xff0c;通过生产、分配、交换和消费的过程&#xff0c;不断地形成和再造价值。从传统的市场经济到现代的智能经济&#xff0…

Shell好用的工具: cut

目标 使用cut可以切割提取指定列\字符\字节的数据 介绍 cut 译为“剪切, 切割” , 是一个强大文本处理工具&#xff0c;它可以将文本按列进行划分的文本处理。cut命令逐行读入文本&#xff0c;然后按列划分字段并进行提取、输出等操作。 语法 cut [options] filename opti…

快速学习安全框架 Springsecurity最新版(6.2)--用户授权模块

简介 上一节Springsecurity 用户认证 Springsecurity 拥有强大的认证和授权功能并且非常灵活&#xff0c;,一来说我们都i有以下需求 可以帮助应用程序实现以下两种常见的授权需求&#xff1a; 用户-权限-资源&#xff1a;例如张三的权限是添加用户、查看用户列表&#xff0c;李…

1.1_1 计算机网络的概念、功能、组成和分类

文章目录 1.1_1 计算机网络的概念、功能、组成和分类&#xff08;一&#xff09;计算机网络的概念&#xff08;二&#xff09;计算机网络的功能&#xff08;三&#xff09;计算机网络的组成1.组成部分2.工作方式3.功能组成 &#xff08;四&#xff09;计算机网络的分类 总结 1.…