动态规划之01背包问题

news2024/11/26 12:37:58

背包练习网址https://www.luogu.com.cn/contest/92872 想要做题的话可以到这里面来进行完成(邀请码:r36l)。注:要输入邀请码才可以进入。

满篇都是干货,有详细的注释和代码,请放心观看。

这就是传说中的 01 背包问题,这个问题看到之后主要有两种思路:

一、贪心做法(错误想法)

        这道题如果没有学过 01 背包问题的话,很容易想成一个贪心的问题,就是讲他的 “性价比"
 从高到低排序(这里的“性价比”指的是 \frac{v_i}{w_i} ),但是我们很容易发现这是错误的,因为将性价比较高的放在前面的话那么不可以尽量的吧空间占用完,所以我们可以显然的发现,这样的方法是错误的,但是如果题目的数据比较水的话还是可以骗很多分的。(在我建立的那个比赛里面,你们可以试一下用这种贪心策略去做,不出意外的话是拿不到满分的)。

        所以这种做法是错误的。

二、01背包问题做法(朴素版本)

        01 背包问题基本上是比较简单的 DP 问题。

        我们通过普通的做 DP 的思路,得先想想应该怎么定义 DP 的状态,我们应该怎么做呢?

        第一步:

        通过以往做 DP 的经验,那么可以不是很困难的得出定义 :dp[i][j] 为选取前  i 个物品,使得总重量不超过 j 的最大总价值(这个是很重要的点,如果在做动态规划的时候没有想出这个点,那么基本上这道题就没有什么希望了)。

        第二步:

        这个 DP 数组是否需要加初始化,但是是否定的,因为这道题的 dp 数组一直都在不断的更新,所以这里也不会溢出或者判断错误,可以得出结论:这里的 dp  数组不需要初始化!

        第三步:

        第三步是最难的一步,也是我主要要讲的一步,这一步就是支撑整个代码的“顶梁柱”,那就是:状态应该怎么转移呢

        其实可以发现:枚举每个物品,比如当前枚举到了第 i 个物品,可以发现一共有两种情况:
        一、不选第 i 个物品,那么 dp[i][j] = dp[i-1][j]

        二、选第 i 个物品,那么可以发现 dp[i][j] = dp[i-1][j-v[i]]+w[i]

        解释一下第二种的理解:在前 i-1 个物品中,是总重量不超过 j-v[i] (因为当前枚举到了第 i 个物品,总重量为 j,当前的物品重量为 v[i] ,所以可以发现当前的重量就不可以超过 j-v[i]),所以答案不就呼之欲出了。。(注意最后要加上当前这个物品的总价值)

        总结一下:这道题的状态转移方程式是:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])

        然后这里还要注意一个点,就是在 j-v[i] 的时候有可能会出现负下标,所以需要特判一下 j>=v[i],这就是一个推导状态转移方程式的一个简单的解析,所以就可以写出以下代码(代码中也有较为详细的解释,我在这里就不多说了):

/*f[i][j] = f[i-1][j-v[i]]+w[i]
价值等于前i-1个物品的总容量不超过(j-v[i])的总价值
j-v[i]的含义:最大的容量为j,然而要放进i这个物品,i物
品的重量为v[i],所以前i-1个物品的最大容量只能是(j-v[i])。*/

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1010;

int n, m;//m就是题目中的v
int v[N], w[N], f[N][N];

int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++ ) scanf("%d%d",&v[i],&w[i]);
	for(int i = 1; i <= n; i ++ )
		for(int j = 0; j <= m; j++){
			f[i][j] = f[i-1][j];
			if(j >= v[i]) f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]);
		}
	printf("%d\n", f[n][m]);
	return 0;
}

三、01背包问题做法(一维数组优化版本)

        这种方法会讲的稍微快一点,因为这种方法就是思路二的一个优化版本,所以建议可以先把思路二的想法先看懂,然后再看这里的解析。

        还是熟悉的 DP 三部曲:

        第一步:

         依旧是定义 DP 数组,这里定义成一维的数组,那么就可以轻易的定义出: dp[i] 表示不超过 i 的最大总价值。

        第二步:

         这个 DP 数组是否需要初始化,也就是说他会不会超出边界或者得到不正确的答案,可以简单的画图模拟一下,发现是明显不会覆盖或超出边界的,所以这里的 DP 数组依旧不需要初始化。

        第三步:

         这里我依旧要特别重要的讲一下,其实可以发现不论是二维数组还是一维数组,他们的状态转移其实都是差不多的,所以其实可以直接将上面推出来的那一个状态转移方程给移动到下面来。

        二维数组的状态转移:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]),那么一维数组就可以在二维数组的基础上进行“直接去掉一维”的操作,所以可以得出一维数组的状态转移方程式:dp[j]=max(dp[j],dp[j-v[i]]+w[i]),其实在这里不是很好说明它是对的,所以这里我给大家举一个例子:

比如这里输入三个物品和一个容量为 2 的背包,他们的 v[i] 分别是10,90,100w[i]分别是1,1,2.

然后可以自己尝试一下,发现其实这个答案是错误的。

        为什么是错误的呢?

        其实,并不是状态转移方程错了,而是循环的顺序错了,应该把循环反过来,才可以保证它的正确性,只要稍微想一下可以发现确实如此,因为从后往前进行查找,才不会出现错误的、或者重复的情况,从而就保证了现在这种策略的准确性。

        然后给出参考代码:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>

using namespace std;

const int N = 1010;
int n, m, v[N], w[N], f[N];

int main(){
    scanf("%d%d", &n, &m);
    
    for(int i = 1; i <= n; i++) scanf("%d%d", &v[i], &w[i]);
    for(int i = 1; i <= n; i++)
        for(int j = m; j >= v[i]; j--){/*注意:这里的循环变量要从0开始,因为
这里的这个j表示的是价值不超过j,所以j可以是0*/
            f[j] = max(f[j], f[j-v[i]]+w[i]);
            //我们可以发现每次f[j]数组都要依靠前一个,但是这里明显反了,所以要把循环倒过来
        }
    
    printf("%d\n", f[m]);
    return 0;
}

        注1:在上面给出的两则代码中我在文章中所写的 dp 函数我都用的是 f 函数代替,如有观看的效果不佳,请谅解。

        注2:如有疑问可以在洛谷上私信我也可以就在评论求进行提问,只要我还没有 AFO,都会尽力在 7 日内回复消息,请耐心等待。

谢谢大家支持,这是我的第一篇 CSDN 中的 DP 博客,如果有阅读的不便或者手滑错误请谅解,并且私信/在评论区指出,我会第一时间改进的!!!

觉得博主写的不错的关注支持一下吧!我会继续努力的~

      

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

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

相关文章

[附源码]Python计算机毕业设计二手交易平台管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

数据生态第四弹 | OpenMLDB Hive Connector,架构起数据仓库到特征工程的生态桥梁

导读 近日&#xff0c;OpenMLDB 实现了与开源数据仓库软件 Hive 的连接&#xff0c;继完成与 Kafka、Pulsar、RocketMQ 等实时数据源生态整合后&#xff0c;持续构建离线数据生态&#xff0c;期待建设一个更加全面一体的上下游生态圈&#xff0c;在吸引更多用户的同时也能降低…

【能效管理】AcrelEMS-UT综合管廊能效管理平台解决方案应用分析

平台概述 AcrelEMS-UT综合管廊能效管理平台集电力监控、能源管理、电气安全、照明控制、环境监测于一体&#xff0c;为建立可靠、安全、高效的综合管廊管理体系提供数据支持&#xff0c;从数据采集、通信网络、系统架构、联动控制和综合数据服务等方面的设计&#xff0c;解决了…

DC-4靶场教程

探测目标靶机&#xff0c;获取到ip arp-scan -l扫描开放的端口 masscan -p1-65535 192.168.250.213 --rate10000使用nmap获取端口、服务版本、系统等详细信息 nmap -sC -sV -p- -A -T4 192.168.250.213查看使用的中间件 whatweb http://192.168.250.213/访问80端口 没有其…

NLP 模型“解语如神”的诀窍:在文本分类模型中注入外部词典

一. 引言 现实世界的文本表述如恒河沙数&#xff0c;以惊人的速度变换着&#xff0c;人工智能&#xff08;AI&#xff09;在快速识别形形色色的文本之前&#xff0c;必须经过充足的训练数据洗礼。然而&#xff0c;面对复杂多变的文本表述&#xff0c;NLP 模型往往无法从有限的…

架构设计(一):从单服务器模式到负载均衡设计

架构设计&#xff08;一&#xff09;&#xff1a;从单服务器模式到负载均衡设计 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;架构设计&#xff08;一&#xff09;&#xff1a;从单服务器模式到负载均衡设计 CSDN&#xff1a;架构设计&#xff08;一&…

latex设置citation显示作者+年份

如果是bib文件分开放&#xff0c;并且每个引用都明确写了author和year&#xff0c;那么直接\citep 就可以&#xff0c;就能产生(abc et al., 2015) 这种格式, 如果你不想要圆括号&#xff0c;可以使用\usepackage[square]{natbib}, 也可以使用\setcitestyle{authoryear,open{(}…

java计算机毕业设计基于安卓Android的论坛App

项目介绍 首先,论文一开始便是清楚的论述了系统的研究内容。其次,剖析系统需求分析,弄明白“做什么”,分析包括业务分析和业务流程的分析以及用例分析,更进一步明确系统的需求。然后在明白了系统的需求基础上需要进一步地设计系统,主要包罗软件架构模式、整体功能模块、数据库设…

【Kubernetes】Pod数据存储

kubernetes&#xff0c;是一个全新的基于容器技术的分布式架构领先方案&#xff0c;是谷歌严格保密十几年的秘密武器----Borg系统的一个开源版本&#xff0c;于2014年9月发布第一个版本&#xff0c;2015年7月发布第一个正式版本。 kubernetes的本质是一组服务器集群&#xff0…

时间序列异常检测(adtk)

1. 获取时间序列数据 未安装adtk的先安装&#xff1a;pip install adtk 2.阈值检测 adtk.detector.ThresholdAD(lowNone, highNone) 参数&#xff1a; low&#xff1a;下限&#xff0c;小于此值&#xff0c;视为异常 high&#xff1a;上限&#xff0c;大于此值&#xff0c;视为…

基于java+springboot+mybatis+vue+mysql的4S店车辆管理系统

项目介绍 任何系统都要遵循系统设计的基本流程&#xff0c;本系统也不例外&#xff0c;同样需要经过市场调研&#xff0c;需求分析&#xff0c;概要设计&#xff0c;详细设计&#xff0c;编码&#xff0c;测试这些步骤&#xff0c;基于java语言设计并实现了4S店车辆系统&#…

回溯3-----BFS(一石激起千层浪)

文章目录广度优先搜索力扣429-----N叉树的层序遍历力扣994-----腐烂的橘子力扣127 -------单词接龙力扣725------打开转盘锁广度优先搜索 需要借助 队列 来解决问题 例如二叉树的层序遍历 &#xff1a; 1&#xff0c; 将根节点入队 2&#xff0c; 队列出队的时候&#xff0c; …

第一章 OAuth2.0规范(史上最详细解释)——介绍

目录 一、简介 二、角色 二、协议流程 三、授权许可 1、授权码 2、隐式许可 3、资源所有者密码凭据 4、客户端凭据 四、访问令牌 五、刷新令牌 六、TLS版本 七、HTTP重定向 八、互操作性 九、符号约定 一、简介 在传统的客户端-服务器身份验证模式中&#xff0c;客…

docker搭建redis三种集群模式

文章目录一、主从复制1、新建修改配置文件2、执行命令启动redis3、启动客户端测试二、哨兵模式Sentinel1、修改配置文件2、启动sentinel进程3、测试三、集群模式Cluster1、搭建集群1.1、创建集群挂载文件1.2、修改配置文件1.3、docker启动先六个redis实例1.4、构建集群1.5、查看…

【Canvas系列】基础入门(一)

&#x1f4da; Canvas基础入门 &#x1f525; 收藏关注随时温习 &#x1f4bb; 在线地址方便浏览 在线学习/演示地址 Canvas API 提供了一个通过JavaScript 和 HTML的元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面 —— MDN 一…

shell-条件

条件可以用test或【命令 符号和被检查的条件之间要留有空格 &#xff1a; if [ -f filename *** ] if 和then用于同一行 if [ ]; then 字符串比较&#xff1a; if [ string1 string2 ] 两个字符串相等为真 if [ string1 ! string2 ] 两个字符串不相等为假 …

web自动化测试:Selenium+Python基础方法封装

推荐阅读&#xff1a; [内部资源] 想拿年薪30W的软件测试人员&#xff0c;这份资料必须领取~ Python自动化测试全栈性能测试全栈&#xff0c;挑战年薪40W 正文 01 目的 web自动化测试作为软件自动化测试领域中绕不过去的一个“香饽饽”&#xff0c;通常都会作为广大测试从…

Fiddler(4):在fiddler会话栏中添加IP列,设置抓HTTPS的请求证书安装

Fiddler(4)&#xff1a;在fiddler会话栏中添加IP列 在fiddler会话栏中添加IP列 1、点击菜单栏rules——customize rules…或者在右侧响应栏中点击FiddlerScript栏&#xff1b;如下图&#xff1a; 2、ctrlf搜索“static function main” 3、在main函数里加入下面一行代码&…

Kafka极客 - 09 Kafka Java Consumer 多线程开发实例

文章目录1. Kafka Java Consumer 设计原理2. 多线程方案3. 代码实现4. 问题思考目前&#xff0c;计算机的硬件条件已经大大改善&#xff0c;即使是在普通的笔记本电脑上&#xff0c;多核都已经是标配了&#xff0c;更不用说专业的服务器了。如果跑在强劲服务器机器上的应用程序…

对称美学 华为OD真题 100

import java.util.Scanner; import java.util.*; import java.util.stream.Collectors; import java.math.BigInteger;class Main {public static void main(String[] args) {// 处理输入Scanner in new Scanner(System.in);int t in.nextInt();long[][] cases new long[t][…