C++动态规划及九种背包问题

目录

目录

一,动态规划

一),动态规划的定义

二),动态规划其他的相关概念(也是使用条件)

1,重叠子问题

2, 最优子结构

3,无后效性

三),动态规划的优势

四),动态规划的使用流程:

二,背包问题

(一)0/1背包

经典题面:

解决方法

1.二维数组

        (核心代码)

2.滚动数组

3.一维数组

详细代码

总结:

(二)完全背包问题

经典题面:

状态方程:

未优化版:

优化版本:

状态转移方程

最终代码:

(三)多重背包

经典题面:

解决方法:

转化!!

1,01背包法:

2,完全背包法:

3,二进制优化。

例题:

题目要求

代码

(四)混合背包

经典题面:

例题:​编辑

(五)二维费用背包

经典题面:

分析:

例题:

代码 

(六)分组背包

经典题面:

分析:

代码 


一,动态规划

一),动态规划的定义

动态规划(Dynamic Programming,简称DP)是运筹优化的一个重要方法,它是求解具有重复子问题和最优子结构性质的问题的方法。通常通过把原问题分解为相对简单的子问题的方式来求解复杂问题的方法。动态规划常常适用于有重叠子问题最优子结构性质的问题。

那么上面那两坨是什么呢?当然不止有这两坨东西

二),动态规划其他的相关概念(也是使用条件

1,重叠子问题

重叠子问题是指在对问题进行求解时,每次产生的子问题并不总是新问题,有些子问题会被重复计算多次。‌ 这种性质在‌动态规划问题中得到了利用,通过只计算一次每个子问题并将其结果保存在一个表格中,当再次需要计算已经计算过的子问题时,只需查看表格中的结果,从而提高了效率。‌

2, 最优子结构

最优子结构是一个重要特点,它表示问题的最优解可以通过解决问题的子问题得到。具体来说,如果一个问题的最优解包含了其子问题的最优解,那么这个问题就具有最优子结构。

3,无后效性

是指如果在某个阶段上过程的状态已知,则从此阶段以后过程的发展变化仅与此阶段的状态有关,而与过程在此阶段以前的阶段所经历过的状态无关。【说白了,就是当前选择不影响未来】

三),动态规划的优势

1.存放子问题答案,保证求解的正确性。

2.用递推方法,解决重复子问题的干扰。

3.按性质划分集合,减少子问题的数量。

4,时间复杂度低(更高效):动态规划本质上就是递推的过程,对于重复的子问题非常的高效。而DP将子问题规模缩小(相同性质的子问题合并视为一个)将复杂度大大降低。

四),动态规划的使用流程

1,划定子问题,描述状态

2,确定状态转移方程

3,确定初始值

4,确定遍历顺序

二,背包问题

说到动态规划,就不可避免地提到背包问题。 

(一)0/1背包

经典题面:

有n件物品,v的背包空间。依次有物品价值为w1,质量(体积)(占用背包空间)p1。求出可以装下的价值最高的总价值。(也有题目改为时间、精力等等,原理一样)

【用通俗易懂(人话)说就是在有限的空间下尽可能装更值钱的物品(财宝)】所以就要合理利用背包空间。

解决方法
1.二维数组

建立dp数组。dp[i(第i件物品)][j(当前可以使用的背包空间)]也就是在只选择前i件物品时,背包空间不超过j的最大价值。

对于每一个点,都可以选择不装当前物品,保持之前所装的最大价值,也可以选择装下来。因为是最大价值,所以就可以使用一个max(不变,拾取当前物品)求出现在的最大值再进行更新。最后输出最后一个点(dp[n][v])就是最终答案 

        (核心代码)

【空间复杂度:nv】

for(int i=1;i<=n;i++){for(int j=0;j<=m;j++){   dp[i][j]=dp[i-1][j];if(v[i]<=j)    dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);}
}

这个过程其实就是一个填表格的过程,但是仔细想就会发现,每个点只与自己上一行左侧的点有关联,其余的点都没有被再次更新。节省空间,启动!!!

2.滚动数组

只与上一行有关联那就见一个dp[2][i]的数组就好了啊,那这样新的maxn就赋值在i%2的行里面了。

这样空间复杂度就被更新到2v

3.一维数组

还可以节省,只是用一个一维数组就可以解决了。如果从左往右更新显然也是不行的。每个点都与自己的左上点有关联,如果从左往右就会先将左边更新了就不行了。那就从左往右,此时左边的还保存着上个阶段(i-1)时的数据,直接使用不会出问题,而且当不需要更新,就直接使用原来的数据也不会有问题。这样空间复杂度(单个dp数组)就压缩到了O(n)

详细代码
	for(int i=1;i<=n;i++){//循环模拟n件物品cin>>w[i]>>v[i];//输入代价和价值for(int j=t;j>=w[i];j--){//从背包末尾到当前代价模拟dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//当前点的价值就是原先当前下标的价值和左边点的价值加上该物品的价值求最大值。}}

总结:

0/1背包时间复杂度为O(nv)(重复遍历n件物品,每次遍历背包空间)

空间复杂度最低O(v)

(二)完全背包问题

经典题面:

有N件物品和一个能背重量为W的背包,第i件物品的重量为w(weight)[i],价值为v(value[i])。每件物品有无数个,求怎样可以使背包物品价值总量最大。【人话:可以无限取同一个物品(当然是价值最高的),使自己背包价值最大】

状态方程:

f[i,j]=max\left\{\begin{matrix} && \\ f[i-1][j] & &\\f[i-1][j-v_{i}] & &\\f[i-1][j-k*v_{i}]+k*w_{i} \end{matrix}\right.

我们需要模拟该物品是否要选,如果要选那么选择几个。

边界是f[0][0]

目标是f[N][M]

未优化版:
for(int i=1;i<=n;i++) {//模拟物品 for(int j=0;j<=m;j++){//模拟背包空间 for(int k=0;k*v[i]<=j;k++){//模拟选择第i件物品选择的数量。 f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+w[i]*k);//更新最大价值 }}
}

可以发现,因为为优化的朴素版本使用了三层循环嵌套,所以时间复杂度比较高。还是按照01背包优化的方法将f数组设为1维数组。这样不仅降低了空间复杂度,也降低了遍历时的时间复杂度。

优化版本:
状态转移方程

就是:f[j]=max{f[j],f[j-k*c]+k*w}(0<=k*c<=v)

最终代码:
for(int i=1;i<=n;i++){//模拟n件物品 for(int j=v[i];j<=m;j++){//模拟背包空间 f[j]=max(f[j],f[j-v[i]]+w[i]);//按照公式进行更新 }
} 
cout<<f[V];//统计表格的最后一个位置 

(三)多重背包

经典题面:

给定n种物品,以及一个容量大小为m的背包,然后给出n个物品的体积、价值及个数,求背包最大价值是多少,也就是选择总体积不超过m的物品,然后使总价值最大。【就是在想着怎么获取最多钱数的同时还有拿取的数量限制】

解决方法:

多重背包可以进行亿点点的转化,因为01背包和完全背包的状态方程都是:F [j]=max(F [j], F [ j- v [i] ]+ w [i])。

转化!!
1,01背包法:

将多个重复的小物件进行整合,使其变成一个大物体,这样只需要考虑是否选择这个大的物体就变成了01背包。假设原小物体的体积为wi,价值为vi,数量为si。设将k件该物体进行组合,就得到了一个大小为k*w1,价值为k*vi的物体,这个时候就考虑是否选择这个新的大物体。

2,完全背包法:

当同一种物体的总体积连背包里都塞不下了,也就是si*wi>M这个时候就可以考虑随便拿,我们连 s 件物品都不可能拿完背包就已经塞不下了。这样只需要特判一下就可以使用完全背包来做了。

3,二进制优化。

如果要拿512件物品,按照转化成01背包的方法做,需要从拿1件枚举到拿512件的,而二进制优化就一些倍增思想,我们把拿多少件物品分为拿1   2   4   8  16  ...  256  512 ... 2^n 件,我们枚举的时候就枚举9次 就到了512件了。而且无论是多少件,我们总能用一些数的组合来表示。这样就可以节省时间。

例题:

题目要求

这道题就是典型的多重背包问题,按照上面的思路进行DP即可。

代码
#include<bits/stdc++.h>//万能头文件 
#pragma GCC s//日常优化 
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC fastusing namespace std;
const int N=1e4+5,M=4e4+5;
int n,m,a,b,c,cnt=0,v[N],w[N],f[M];
long long read() {//快读压时间 int x=0,f=1;char c=getchar();while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}return x*f;
}int main(){n=read();m=read();for(int i=1;i<=n;i++){b=read();a=read();c=read();int t=1;while(c>=t){w[++cnt]=t*a;v[cnt]=t*b;c-=t;//总数量减去合成数量 t*=2;//t倍增 }if(c){//如果还有剩余的,就将剩余件数合成为新的个体 w[++cnt]=c*a;v[cnt]=c*b;}}for(int i=1;i<=cnt;i++)for(int j=m;j>=w[i];j--)f[j]=max(f[j],f[j-w[i]]+v[i]);//转化完成后就按照01背包进行DP printf("%d",f[m]);//输出最后一个位置得到的最大值。 
}

(四)混合背包

顾名思义,就是将上面的三种背包问题掺和在一起,根据物品的数量si进行选择。

经典题面:

给定n种物品,以及一个容量大小为m的背包,然后给出n个物品的体积、价值,求背包最大价值是多少,也就是选择总体积不超过m的物品,然后使总价值最大。
区别:本题的物品分为三种,1、只有一个;2、有无限多个;3、有x个

这种类型的背包问题就结合一道例题来进行分析

例题:

分析:因为是01、完全和混合背包这三种杂在一起的,所以根据可以选择的物品数量进行选择。 

代码:

#include<bits/stdc++.h>
#pragma GCC s
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC fastusing namespace std;
long long read() {int x=0,f=1;char c=getchar();while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}return x*f;
}int main(){int n,m;cin>>n>>m;int v[1010]={},w[1050]={},s[1050]={},f[1100]={};//体积,价值,数量 for(int i=1;i<=n;i++){cin>>w[i]>>v[i]>>s[i];//首先按照题目要求的:体积,价值,数量进行输入 if(s[i]==-1){//在题目中说道,当si是-1的时候,就按照01背包来进行DP,那么就将它的数量转换成1用多重背包进行DP。 s[i]=1;}}for(int i=1;i<=n;i++){//从1到n模拟物品的编号 if(s[i]==0){//按照题目要求特判一下是否需要用到完全背包 for(int j=w[i];j<=m;j++) {//按照完全背包的模板进行更新 f[j]=max(f[j],f[j-w[i]]+v[i]);}}else {//不是完全背包就是多重背包 for(int l=1;l<=s[i];l++)for(int j=m;j>=l*w[i];j--) {f[j]=max(f[j],f[j-w[i]]+v[i]);}}}printf("%d",f[m]);//最后输出将背包价值最大化的最大值 return 0;
}

(五)二维费用背包

这种背包就是一个物品受两个因素制约:例如:重量、体积;长度、宽度。等等

经典题面:

给定n nn个物品,以及一个容量大小为m mm、能承受质量为W WW的背包,然后给出n nn个物品的体积、价值及质量,求背包最大价值是多少,也就是选择总体积不超过m mm的物品,然后使总价值最大。

分析:

        费用加了一维,只需状态也加一维即可。设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。
  状态转移方程就是:f [i][v][u]  =  max {  f [i-1] [v] [u]   , f [i-1] [v-a[i]] [u-b[i]]  + c[i] }。

还是按照之前优化的方式将第一维优化掉。 

例题:

代码 

#include<bits/stdc++.h>
const int N=1e4+5;
using namespace std;
int v[N]={0},w[N]={},m[N]={},f[N][N]={};//因为数组比较大,所以要放在外面 
long long read() {//快读 int x=0,f=1;char c=getchar();while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}return x*f;
}int main(){int n,V,h;n=read();V=read();h=read();//背包容积,背包最大承重 for(int i=1;i<=n;i++){cin>>v[i]>>m[i]>>w[i]; //体积,重量,价值 }for(int i=1;i<=n;i++){//模拟物品 for(int j=V;j>=v[i];j--){//模拟体积 for(int k=h;k>=m[i];k--){//模拟质量 f[j][k]=max(f[j][k],f[j-v[i]][k-m[i]]+w[i]);//在原来的一维数组的基础上增加到二维。状态转移放长也要适当修改 }}}cout<<f[V][h];//不光是体积的V了,还要质量的最大h,这样就是可以拿到的受两个条件制约的最大价值 
} 

(六)分组背包

经典题面:

给N组物品,然后每一组你至多选择一个物品(也可以不选),每个物品都有自己的体积和价值,容里为M的背包,用这个背包装物品,使得物品价值总和最大.

分析:

其实就类似于01背包,对于一个物品有两种决策选或不选,但是分组背包是在01背包的基础上对物品进行了分组,并且每一组最多只能选择一个物品,所以不妨用01背包的思想去思考分组背包.

状态转移方程:if(j>=w[i][k])    f[j]=max(f[j],f[j-w[i][k]]+w[i][k]); 
例题:

 

代码 

#include<bits/stdc++.h>
using namespace std;
int c[1005]={},v[1005][1005]={},w[1005][1005]={},f[10006]={}; 
long long read() {int x=0,f=1;char c=getchar();while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}return x*f;
}int main(){int n,m;n=read(),m=read();for(int i=1;i<=n;i++){c[i]=read();for(int j=1;j<=c[i];j++){w[i][j]=read(),v[i][j]=read();}}f[0]=0;for(int i=1;i<=n;i++){//模拟n件物品 for(int j=m;j>=0;j--){//模拟背包空间 for(int k=1;k<=c[i];k++){//模拟每个分组的c【i】件物品 if(j>=w[i][k]){//如果剩余空间大于新物品的体积 f[j]=max(f[j],f[j-w[i][k]]+v[i][k]); //按照状态转移方程进行更新 }}}}cout<<f[m];
}

 未完待续!!!!!!!!!!!!!!!!!!!

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

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

相关文章

dompdf导出pdf中文乱码显示问号?、换行问题、设置图片大小

环境&#xff1a;PHP 8.0 框架&#xff1a;ThinkPHP 8 软件包&#xff1a;phpoffice/phpword 、dompdf/dompdf 看了很多教程&#xff08;包括GitHub的issue、stackoverflow&#xff09;都没有解决、最终找到解决问题的根本&#xff01; 背景&#xff1a;用Word模板做转PDF…

【JavaEE初阶】IP协议

目录 &#x1f4d5;引言 &#x1f334;IP协议的概念 &#x1f333;IP数据报 &#x1f6a9;IPv4协议头格式 &#x1f6a9;IPv6的诞生 &#x1f3c0;拓展 &#x1f384;IP地址 &#x1f6a9;IP地址的格式&#xff1a; &#x1f6a9;IP地址的分类 &#x1f3c0;网段划分…

【第57课】SSRF服务端请求Gopher伪协议无回显利用黑白盒挖掘业务功能点

免责声明 本文发布的工具和脚本&#xff0c;仅用作测试和学习研究&#xff0c;禁止用于商业用途&#xff0c;不能保证其合法性&#xff0c;准确性&#xff0c;完整性和有效性&#xff0c;请根据情况自行判断。 如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利&#xff0…

Unity动画模块 之 Animator中一些常见参数

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正 我发现我忘了写Animator了&#xff0c;正好有些不常用的参数还没怎么认识,笔记来源于唐老狮 1.状态窗口参数 2.连线参数…

如何使用ssm实现学生公寓管理系统的设计与实现

TOC ssm106学生公寓管理系统的设计与实现jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;…

Qt第十八章 XML和Json格式解析

文章目录 JSON格式解析Json生成案例 XML简介与HTML的区别格式XML解析流的方式DOM XML生成 JSON与XML的区别比较 JSON 格式 JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名 六个构造字符 开始和结束数组&#xff1a;[ ]开始和结束对象&#x…

简易STL实现 | Vector的实现

1、内存管理 1、std::vector 维护了两个重要的状态信息&#xff1a;容量&#xff08;capacity&#xff1a;当前 vector 分配的内存空间大小&#xff09;和大小&#xff08;size&#xff1a; vector 当前包含的元素数量&#xff09; 2、当容量不足以容纳新元素时&#xff0c;s…

SSH 远程登录报错:kex_exchange_identification: Connection closed.....

一 问题起因 在公司,使用ssh登录远程服务器。有一天,mac终端提示:`kex_exchange_identification: Connection closed by remote host Connection closed by UNKNOWN port 65535`。 不知道为啥会出现这样的情形,最近这段时间登录都是正常的,不知道哪里抽风了,就提示这个。…

巴恩斯利蕨数学公式及源码实现——JavaScript版

为什么要写这篇文章 本篇接《张侦毅&#xff1a;巴恩斯利蕨数学公式及源码实现》。之前文章中源码的编程语言用的是Java&#xff0c;JDK的版本为8&#xff0c;现在我的JDK版本已经升级到22了&#xff0c;在新版本JDK中&#xff0c;原来的JApplet方法已经被废弃&#xff0c;不能…

鸿蒙实现在图片上进行标注

一.实现思路 现在需求是&#xff1a;后端会返回在这张图片上的相对位置&#xff0c;然后前端这边需要在图片上进行标注&#xff0c;就是画个框框圈起来&#xff0c;返回的数据里包括当前框的x,y坐标和图片大小&#xff0c;大体思路就是使用canvas绘制&#xff0c;使用鸿蒙的st…

vue-element-admin解决三级目录的KeepAlive缓存问题(详情版)

vue-element-admin解决三级目录的KeepAlive缓存问题&#xff08;详情版&#xff09; 本文章将从问题出现的角度看看KeepAlive的缓存问题&#xff0c;然后提出两种解决方法。本文章比较详细&#xff0c;如果只是看怎么解决&#xff0c;代码怎么改&#xff0c;请前往配置版。 一…

零工市场小程序应该有什么功能?

数字经济现如今正飞速发展&#xff0c;零工市场小程序在连接雇主与自由职业者方面发挥着越来越重要的作用。一个高效的零工市场小程序不仅需要具备基础的信息发布与匹配功能&#xff0c;还应该涵盖交易管理、安全保障以及个性化服务等多个方面。 那么&#xff0c;零工市场小程…

Ubuntu22.04下安装LDAP

目录 1 简单说明2 安装配置2.1 安装1、安装前准备2、安装 OpenLADP3、配置OpenLDAP4、设置基本组5、添加新组5、添加 OpenLDAP 用户 2.2 安装 LDAP 帐户管理器1、安装2、配置 LDAP 帐户管理器 3 简单使用3.1 创建一个组3.2 创建一个用户 总结 1 简单说明 之前写过在Centos下的…

nginx和tomcat负载均衡,动静分离

文章目录 一&#xff0c;tomcat1.tomca用途2.tomcat重要目录 二&#xff0c;nginx1.Nginx应用2.nginx作用3.nginx的正向代理和反向代理3.1正向代理3.2反向代理(单级)3.3反向代理(多级) 4.nginx负载均衡4.1Nginx支持的常见的分流算法1. 轮询(Round Robin):2.最少连接数(LeastCon…

[MRCTF2020]Hello_ misc

解压得一个png图片和一个flag.rar 图片拖入010editor 选择带zip头的这段蓝色全部复制&#xff0c;file-new-new Hex File&#xff0c;黏贴到新文件&#xff0c;另存为为1.zip 要密码,线索中断&#xff08;当然try to restore it.png&#xff0c;隐藏了zip压缩包&#xff0c;可…

uniapp - plugins的组件配置使用

点击进入到uniapp中mp-weixin的配置中 点击进入小程序的plugin的配置 在项目中&#xff0c;我们可引用插件的使用&#xff0c;例如一些快递100&#xff0c;点餐插件的业务引入 添加插件 在使用插件前&#xff0c;首先要在小程序管理后台的“设置-第三方服务-插件管理”中添加…

java ssl使用自定义证书或忽略证书

1.证书错误 Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 2.生成客户端证书 openssl x509 -in <(openssl s_client -connect 192.168.11.19:8101 -prexit 2>/dev/null) -ou…

C HTML格式解析与生成

cmake报错替换 if(NOT MyHTML_BUILD_WITHOUT_THREADS OR NOT MyCORE_BUILD_WITHOUT_THREADS) set(CMAKE_THREAD_PREFER_PTHREAD 1) if (WIN32) set(CMAKE_USE_WIN32_THREADS_INIT ON) set(CMAKE_THREAD_PREFER_PTHREADS TRUE) set(THREADS_PR…

windows配置jmeter定时任务

场景&#xff1a; 需要让脚本在指定的执行 步骤&#xff1a; 准备jmeter脚本&#xff0c;保证在命令行中可以调用脚本且脚本运行正常&#xff1a;"C:\Apache\jmeter\bin\jmeter.bat" -n -t C:\tests\test_plan.jmx -l C:\tests\results.jtl -t : 指定执行jmeter脚…

异步交互技术Ajax-Axios

目录 一、同步交互和异步交互 二、Ajax 1.概述 2.如何实现ajax请求 三、异步传输数据乱码的问题 regist.html页面代码 服务端代码处理 四、Axios 1. Axios的基本使用 &#xff08;1&#xff09;引入Axios文件 &#xff08;2&#xff09;使用Axios发送请求&#xff0…