动态规划:01背包问题(一)

本题力扣上没有,是刷的卡码网第46题感兴趣的小伙伴可以去刷一下,是ACM模式。本篇讲解二维dp数组来解决01背包问题,下篇博客将用一维dp数组来解决01背包问题。

题目:

46. 携带研究材料

时间限制:5.000S 空间限制:128MB

题目描述:

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。
小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。

输入描述:

第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。

第二行包含 M 个正整数,代表每种研究材料的所占空间。

第三行包含 M 个正整数,代表每种研究材料的价值。

输出描述:

输出一个整数,代表小明能够携带的研究材料的最大价值。

输入示例:

6 1
2 2 3 1 5 2
2 3 1 5 4 3

输出示例:

5

提示信息:

小明能够携带 6 种研究材料,但是行李空间只有 1,而占用空间为 1 的研究材料价值为 5,所以最终答案输出 5。

数据范围:
1 <= N <= 1000
1 <= M <= 1000
研究材料占用空间和价值都小于等于 1000

思路:

01 背包:

有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

二维dp数组01背包:

重量价值
物品0115
物品1320
物品2430

依然动规五部曲分析。

  1. 确定dp数组以及下标的含义

对于背包问题,有一种写法, 是使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
在这里插入图片描述
要时刻记着这个dp数组的含义,下面的一些步骤都围绕这dp数组的含义进行的,如果哪里看懵了,就来回顾一下i代表什么,j又代表什么。

  1. 确定递推公式

再回顾一下dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

那么可以有两个方向推出来dp[i][j],

  • 不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)
  • 放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值

所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

  1. dp数组如何初始化

关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。

首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。

如图:

在这里插入图片描述

在看其他情况。

状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。

dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。

那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。

当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。

代码初始化如下:

    # 创建二维数组dp,用于保存状态转移的结果
    dp = [[0] * (bag_weight + 1) for _ in range(bag_nums)]
#   for i in range(bag_nums):  此处已把所有dp数组初始为0了 故此步可省略
#       dp[i][0] = 0
    # 初始化第一个背包的状态
    for j in range(weight[0], bag_weight + 1):
        dp[0][j] = value[0]

此时dp数组初始化情况如图所示:
在这里插入图片描述
dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化多少呢?

其实从递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出dp[i][j] 是由左上方数值推导出来了,那么 其他下标初始为什么数值都可以,因为都会被覆盖。

初始-1,初始-2,初始100,都可以!

但只不过一开始就统一把dp数组统一初始为0,更方便一些。

最后初始化代码如下:

def result():
    # 读取输入的数据
    N = [int(x) for x in input().split()]  # 读取背包数量和背包总重量
    weight = [int(x) for x in input().split()]  # 读取每个物品的重量
    value = [int(x) for x in input().split()]  # 读取每个物品的价值
    bag_nums = N[0]  # 背包数量
    bag_weight = N[1]  # 背包总重量
    # 创建二维数组dp,用于保存状态转移的结果
    dp = [[0] * (bag_weight + 1) for _ in range(bag_nums)]
    # 初始化第一个背包的状态
    for j in range(weight[0], bag_weight + 1):
        dp[0][j] = value[0]

到了这里应该有很多人还是不太明白代码为什么这么写,为什么j要从weight[0]开始遍历到bag_weight 结束(这里的代码是直接看着题目描述的输出和输入案例来AC的),我刚开始也不太理解,后来发现还是自己没审好dp数组的初始化步骤和过程,dp数组首先对第一列开始全部初始化为0(我在定义dp数组的时候就已经全部初始化为0了,所以此步骤可以省略)

之后把第一行初始化,这里初始化并不需要排序啊什么的,就取weight数组的第一个元素,看它是否满足小于背包(bag_weight)的重量,如果小于则把该weight数组第一个元素对应重量下的value(价值)赋给dp[0][j],从dp[0][j]到dp[0][bag_weight]全部都赋值为value[0]

  1. 确定遍历顺序

在上述图片中可以看出,有两个遍历的维度(两层for循环):物品与背包重量

那么问题来了,先遍历 物品还是先遍历背包重量呢?

其实都可以!! 但是先遍历物品更好理解。

  1. 举例推导dp数组

来看一下对应的dp数组的数值,如图:
在这里插入图片描述

这里其实就是确认最后的返回值是什么,因为dp数组的含义是最大的价值(不管重量如何变化,取的都是最大的价值总和),所以应该返回dp数组的最后一个元素

return dp[bag_nums - 1][bag_weight]  # 返回最后一个背包的最大价值

代码及详细注释:

def result():
    # 读取输入的数据
    N = [int(x) for x in input().split()]  # 读取背包数量和背包总重量
    weight = [int(x) for x in input().split()]  # 读取每个物品的重量
    value = [int(x) for x in input().split()]  # 读取每个物品的价值
    bag_nums = N[0]  # 背包数量
    bag_weight = N[1]  # 背包总重量
    # 创建二维数组dp,用于保存状态转移的结果
    dp = [[0] * (bag_weight + 1) for _ in range(bag_nums)]
    # 初始化第一个背包的状态
    for j in range(weight[0], bag_weight + 1):
        dp[0][j] = value[0]
    # 状态转移,计算每个背包的最大价值
    for i in range(1, bag_nums):
        for j in range(1, bag_weight + 1):
            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])  # 放入当前物品或者不放入当前物品,取最大值
    return dp[bag_nums - 1][bag_weight]  # 返回最后一个背包的最大价值

if __name__ == '__main__':
    print(result())  # 输出结果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spark---RDD持久化

文章目录 1.RDD持久化1.1 RDD Cache 缓存1.2 RDD CheckPoint 检查点1.3 缓存和检查点区别 2.RDD分区器2.1 Hash 分区&#xff1a;2.2 Range 分区&#xff1a;2.3 用户自定义分区 1.RDD持久化 在Spark中&#xff0c;持久化是将RDD存储在内存中&#xff0c;以便在多次计算之间重…

HDFS WebHDFS 读写文件分析及HTTP Chunk Transfer Encoding相关问题探究

文章目录 前言需要回答的首要问题DataNode端基于Netty的WebHDFS Service的实现基于重定向的文件写入流程写入一个大文件时WebHDFS和Hadoop Native的块分布差异 基于重定向的数据读取流程尝试读取一个小文件尝试读取一个大文件 读写过程中的Chunk Transfer-Encoding支持写文件使…

快慢指针-Floyd判圈算法

对于环形链表是否存在环的做法&#xff0c;普通算法可以通过额外Hash数组来存储链表元素&#xff0c;直到Hash数组中出现重复元素。时间复杂度O(n)&#xff0c;空间复杂度O(n) Floyd判圈算法通过利用快慢指针的移动来实现&#xff0c;时间复杂度O&#xff08;n&#xff09;&am…

Elasticsearch:聊天机器人教程(二)

这是继上一篇文章 “Elasticsearch&#xff1a;聊天机器人教程&#xff08;一&#xff09;”的续篇。本教程的这一部分讨论聊天机器人实现中最有趣的方面&#xff0c;以帮助你理解它并对其进行自定义。 数据摄入 在此应用程序中&#xff0c;所有示例文档的摄取都是通过 flask …

搭建知识付费小程序平台:如何避免被坑,选择最佳方案?

随着知识经济的兴起&#xff0c;知识付费已经成为一种趋势。越来越多的人开始将自己的知识和技能进行变现&#xff0c;而知识付费小程序平台则成为了一个重要的渠道。然而&#xff0c;市面上的知识付费小程序平台琳琅满目&#xff0c;其中不乏一些不良平台&#xff0c;让老实人…

git提交报错:remote: Please remove the file from history and try again.

1. 报错信息 remote: error: File: fba7046b22fd74b77425aa3e4eae0ea992d44998 500.28 MB, exceeds 100.00 MB. remote: Please remove the file from history and try again. git rev-list --objects --all | grep fba7046b22fd74b77425aa3e4eae0ea992d44998 2. 分析原因 e…

单例模式实现最好的方式即枚举实现

单例类作为23种设计模式当中最常用的设计模式&#xff0c;实现方式有很多种&#xff0c;比较流行的是DCL(DoubleCheckLock)双重检查的实现&#xff0c;线程安全&#xff0c;又比较好&#xff0c;除了存在序列化的问题之外&#xff0c;还算不错&#xff0c;如果对DCL模式还不熟悉…

UE5 RPG使用GAS技能系统

之前也介绍过GAS的使用&#xff1a; UE 5 GAS Gameplay Ability System UE 5 GAS 在项目中处理AttributeSet相关 UE 5 GAS 在项目中通过数据初始化 基础的讲解这里不再诉说&#xff0c;有兴趣的可以翻我之前的博客。 接下来&#xff0c;在RPG游戏中实现GAS系统的使用。 GAS系统…

微店商品详情API(micro.item_get)的数据分析和挖掘

随着电商行业的迅猛发展&#xff0c;微店作为电商平台的重要组成部分&#xff0c;提供了丰富的API接口供开发者使用。其中&#xff0c;微店商品详情API&#xff08;micro.item_get&#xff09;是用于获取商品详情的接口&#xff0c;为数据分析和挖掘提供了大量有价值的数据源。…

YOLOv8改进 | 融合改进篇 | 轻量化CCFM + SENetv2进行融合改进涨点 (全网独家首发)

一、本文介绍 本文给大家带来的改进机制是轻量化的Neck结构CCFM配合SENetv2改进的网络结构进行融合改进,其中CCFM为我本人根据RT-DETR模型一比一总结出来的,文中配其手撕结构图,其中SENetV2为网络结构重构化模块,通过其改进主干从而提取更有效的特征,这两个模块搭配在一起…

2.1.2 一个关于y=ax+b的故事

跳转到根目录&#xff1a;知行合一&#xff1a;投资篇 已完成&#xff1a; 1、投资&技术   1.1.1 投资-编程基础-numpy   1.1.2 投资-编程基础-pandas   1.2 金融数据处理   1.3 金融数据可视化 2、投资方法论   2.1.1 预期年化收益率   2.1.2 一个关于yaxb的…

Linux/Uinx 什么是栈帧?

什么是栈帧&#xff1f; 栈帧是计算机内存中的一个独立区域&#xff0c;用于存储程序函数调用过程中的局部变量、参数和返回地址。每当一个函数被调用时&#xff0c;都会在栈上创建一个新的栈帧。函数执行完毕后&#xff0c;对应的栈帧将被销毁。栈帧的概念有助于理解程序函数…

MS-DETR: Efficient DETR Training with Mixed Supervision论文学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2401.03989.pdf 代码地址&#xff08;中稿后开源&#xff09;&#xff1a;GitHub - Atten4Vis/MS-DETR: The official implementation for "MS-DETR: Efficient DETR Training with Mixed Supervision" 摘要 DETR 通过迭代…

canvas绘制图片的三种方法(图文示例)

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

我如何知道我的MinIO集群复制是最新的?

客户可以在任何需要快速、弹性、可扩展对象存储的地方运行 MinIO。MinIO 包括多种类型的复制&#xff0c;以确保每个应用程序都使用最新的数据&#xff0c;无论它在哪里运行。在之前有关批量复制、站点复制和存储桶复制的文章中&#xff0c;我们详细介绍了各种可用的复制选项及…

(N-139)基于springboot,vue宠物领养系统

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vue3element-plus 服务端技术&#xff1a;springbootmybatis-plusr…

MySQL体系结构

MySQL体系结构 MySQL采用的是客户/服务器体系结构&#xff0c;实际是有两个程序&#xff0c;一个是MySQL服务器程序&#xff0c;指的是mysqld程序&#xff0c;运行在存放数据库的机器上&#xff0c;负责在网络上监听并处理来自客户的服务请求&#xff0c;根据这些请求去访问数据…

彩超框架EchoSight开发日志记录

EchoSight开发记录 蒋志强 我会不定期的更新 开发进展。最近更新进展于2024年1月15日 1.背景 由于某些不可抗逆的原因&#xff0c;离开了以前的彩超大厂&#xff0c;竞业在家&#xff0c;难得有空闲的时间。我计划利用这段时间 自己独立 从零开始 搭建一套 彩超系统的软件工…

datax关系数据库插件设计和实现解释

背景 DataX是一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。解决异构数据源同步问题&#xff0c;DataX将复杂的网状的同步链路变成了星型数据链路&#xff0…

GO——cobra

定义 Cobra 是 Go 的 CLI 框架 CLI&#xff0c;command-line interface&#xff0c;命令行界面 使用 注意 第一个cmd的USE即使命名了也没有意义&#xff0c;一般保持和项目名一致。 示例 package mainimport ("fmt""github.com/spf13/cobra" )func …