算法训练营第三十天 | 动态规划 (三)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一、01背包问题理论基础(一)
    • 动态规划五部曲
      • 确定dp数组以及下标的含义
      • 确定递推公式
      • 初始化dp数组
      • 确定遍历顺序
  • 二、01背包问题理论基础(二)
    • 动态规划五部曲
      • 确定dp数组以及下标的含义
      • 确定递推公式
      • 初始化dp数组
      • 确定遍历顺序
      • 代码
  • 三、Leetcode 416. 分割等和子集
    • 动态规划五部曲
      • 确定dp数组以及下标的含义
      • 确定递推公式
      • 初始化dp数组
      • 确定遍历顺序
    • 代码


一、01背包问题理论基础(一)

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

动态规划五部曲

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

这里我们使用二维数组 dp[i][j]。其中 i 代表物品,j 代表背包。

dp 数组表示的含义是,当背包重量为 j 时,我们从 0i 个物品中进行取舍,能获得的最大价值。

本题中 dp 数组的定义十分关键!

确定递推公式

当我们背包重量为 i 的时候,我们一共有两种情况。

  • 背包重量小于物品 i 的重量时,我们无法放入物品,所以我们直接继承上一个状态 dp[i-1][j] ,也就是当背包重量为 j 时,我们从 0i-1 个物品中进行取舍,能获得的最大价值。
  • 当背包重量大于物品 i 的重量时,我们有两种选择,考虑是否选择将物品 i 放入背包内。
  1. 不将物品 i 放入背包内,此时我们也是继承上一个状态 dp[i-1][j]
  2. 将物品 i 放入背包内,此时的状态应为:当前背包容量减去物品 i 的重量在 0i-1 个物品中任意取的最大值再加上物品 i 的价值。,即 dp[i][j] = dp[i - 1][j - weight[i]] + value[i]

我们的目的是获得最大的价值,所以当背包重量大于物品 i 的重量时,我们对两种情况取最大值 max(dp[i - 1][j - weight[i]] + value[i], dp[i-1][j])

最后得出递推公式:

if weight[i-1] > j:dp[i][j] = dp[i-1][j]
else:dp[i][j] = max[dp[i - 1][j - weight[i]] + value[i], dp[i-1][j]]

初始化dp数组

将二维数组理解成为一个表格,行代表物品,列代表背包重量。

我们当前要计算的表格是通过左上方的表格递推出来的,所以我们要对第一列和第一行进行初始化。

第一行代表的是,当背包重量为 j 时,对第一个物品进行取舍,我们能得到的最大价值。
这时我们只有一个物品可以考虑,所以向右遍历,当物品的重量大于等于背包重量时,表示这个物品可以放进背包内,这时右面的表格就初始化为第一个物品的价值。左面的表格就初始化为 0

第一列代表的是,当背包重量为 0 时,对前 i 个物品进行取舍能获得的最大价值。背包重量是 0,当然什么物品也放不进去,所以第一列全部初始化为 0

其余位置都是通过递推公式计算得出,所以初始值无所谓,全部初始化为 0

确定遍历顺序

很显然,我们需要两层 for 循环,那么到底是先遍历物品还是先遍历背包呢?

本题中先遍历背包和先遍历物品都可以!

  • 先遍历物品的方式更符合动态规划的自底向上的求解思路。先考虑只有一个物品时,对于不同背包容量的最大价值,然后逐步增加物品数量,计算在当前物品加入的情况下,不同背包容量的最大价值。这种方式可以清晰地看到状态是如何逐步转移和构建的
  • 先遍历背包的方式则是先固定背包容量,然后看在这个容量下,不同物品组合能达到的最大价值。虽然代码执行顺序不同,但最终也能正确计算出所有状态的最大价值。

代码:

n, bagweight = map(int, input().split())weight = list(map(int, input().split()))
value = list(map(int, input().split()))dp = [[0] * (bagweight + 1) for _ in range(n)]for j in range(weight[0], bagweight + 1):dp[0][j] = value[0]for i in range(1, n):for j in range(bagweight + 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])print(dp[n - 1][bagweight])

二、01背包问题理论基础(二)

对于递归问题,有的时候我们当前状态只是通过前面几个状态进行推导,如果定义一个很长的数组,那么大部分的空间是被浪费的,滚动数组就是在空间上进行优化的一种方法。

举一个例子,斐波那契数列问题。我们的状态转移方程是 dp[i]=dp[i-1]+dp[i-2 。那么当前状态是由前两个状态推导而来,当我们推导 dp[3] 的时候,dp[0] 就已经用不到了,所以我们完全可以将推导得到的 dp[3] 放在 dp[0] 的位置,这样我们只开辟了三块空间就能完成整个计算,大大节省了空间,这就是滚动数组的思想。

对于 01 背包问题,我们当前层的状态都是由上一层的状态推导而来,所以也可以使用滚动数组来进行优化。

动态规划五部曲

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

dp[j] 表示背包重量为 weightp[j] 时,可以获得的最大价值。

确定递推公式

二维dp数组的递推公式为: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
一维dp数组,其实就上一层 dp[i-1] 这一层 拷贝的 dp[i]来。
所以在 上面递推公式的基础上,去掉i这个维度就好。

递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

以下为分析:

dp[j] 为 容量为j的背包所背的最大价值。

dp[j] 可以通过 dp[j] - weight[i]] 推导出来,dp[j] - weight[i]] 表示容量为 j - weight[i] 的背包所背的最大价值。

dp[j - weight[i]] + value[i] 表示 容量为 [j - 物品i重量] 的背包 加上 物品 i 的价值。(也就是容量为 j 的背包,放入物品 i 了之后的价值即:dp[j]

此时 dp[j] 有两个选择,一个是取自己 dp[j] 相当于 二维dp数组中的 dp[i-1][j] ,即不放物品 i,一个是取 dp[j - weight[i]] + value[i] ,即放物品 i ,指定是取最大的,毕竟是求最大价值。

初始化dp数组

dp数组初始化的时候,都初始为 0 即可。

确定遍历顺序

我们在二维dp数组的时候,遍历顺序没有要求,但是对于一维数组,这里有严格的顺序。
我们的滚动数组的思想是当前层是由上一层拷贝而来,而每一层代表的是物品,所以我们要先便遍历物品,再遍历背包重量。

在遍历背包重量的时候,一定要倒序遍历。

因为在某一层,当前数值是由他左面推导而来,如果我们正序遍历的话,新的值会覆盖掉原来的值,这样也就导致了某一个物品被多次使用。

代码

n, bagweight = map(int, input().split())
weight = list(map(int, input().split()))
value = list(map(int, input().split()))dp = [0] * (bagweight + 1)  # 创建一个动态规划数组dp,初始值为0dp[0] = 0  # 初始化dp[0] = 0,背包容量为0,价值最大为0for i in range(n):  # 应该先遍历物品,如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品for j in range(bagweight, weight[i]-1, -1):  # 倒序遍历背包容量是为了保证物品i只被放入一次dp[j] = max(dp[j], dp[j - weight[i]] + value[i])print(dp[bagweight])

三、Leetcode 416. 分割等和子集

给你一个 只包含正整数的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5][11]

引用:

原文链接:https://programmercarl.com/0416.%E5%88%86%E5%89%B2%E7%AD%89%E5%92%8C%E5%AD%90%E9%9B%86.html
题目链接:https://leetcode.cn/problems/partition-equal-subset-sum/description/
视频讲解:https://www.bilibili.com/video/BV1rt4y1N7jE/

解决的问题是判断集合里能否出现总和为 sum / 2 的子集,这相当于有一个只能装重量为 sum / 2 的背包,商品就是集合里的数字,要判断这些数字能否把背包装满。
对于这些数字商品而言,它们只有一个维度,即重量等于价值。当这些数字能装满承载重量为 sum / 2 的背包时,背包的价值也为 sum / 2
所以此问题可转化为装满承载重量为 sum / 2 的背包时,其最大价值是多少,若最大价值是 sum / 2,就说明背包正好被商品装满,由于商品是数字且重量和价值相同,因此可以直接用 01 背包来解决这个问题。

动态规划五部曲

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

dp[j] 表示背包重量为 weightp[j] 时,可以获得的最大价值。

确定递推公式

背包问题的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
那么这里重量和价值在同一个维度,所以我们的递推公式改写为
dp[j]=max(dp[j], dp[j-nums[i]]+nums[i])

初始化dp数组

dp数组初始化的时候,都初始为 0 即可。

确定遍历顺序

和我们01背包问题中的一维滚动数组遍历方式一样。

代码

class Solution:def canPartition(self, nums: List[int]) -> bool:total_sum = sum(nums)if total_sum % 2 != 0:return Falsetarget = int(total_sum / 2)dp = [0] * (target+1)for i in range(len(nums)):for j in range(target, nums[i]-1, -1):dp[j] = max(dp[j], dp[j-nums[i]]+nums[i])return dp[target]==target

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

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

相关文章

玩机搞机基本常识-------小米OLED屏幕机型怎么设置为永不休眠_手机不息屏_保持亮屏功能 拒绝“烧屏” ?

前面在帮一位粉丝解决小米OLED机型在设置----锁屏下没有永不休眠的问题。在这里&#xff0c;大家要明白为什么有些小米机型有这个设置有的没有的原因。区分OLED 屏幕和 LCD屏幕的不同。从根本上拒绝烧屏问题。 OLED 屏幕的一些优缺点&#x1f49d;&#x1f49d;&#x1f49d; …

PostgreSQL使用LIKE右模糊没有走索引分析验证

建表&数据初始化可参考PostgreSQL 分区表——范围分区SQL实践 背景&#xff1a; 给t_common_work_order_log的handle_user_name新建索引后&#xff0c;使用LIKE右模糊匹配查询时&#xff0c;发现走的全表扫描 CREATE INDEX order_log_handle_user_name_index ON t_commo…

【vue】【element-plus】 el-date-picker使用cell-class-name进行标记,type=year不生效解决方法

typedete&#xff0c;自定义cell-class-name打标记效果如下&#xff1a; 相关代码&#xff1a; <el-date-pickerv-model"date":clearable"false":editable"false":cell-class-name"cellClassName"type"date"format&quo…

《Learning Langchain》阅读笔记8-RAG(4)在vector store中存储embbdings

什么是 vector store&#xff1f; 与专门用于存储结构化数据&#xff08;如 JSON 文档或符合关系型数据库模式的数据&#xff09;的传统数据库不同&#xff0c;vector stores处理的是非结构化数据&#xff0c;包括文本和图像。像传统数据库一样&#xff0c;vector stores也能执…

用api的方式调用本地下载好的大模型(以llama为例,不是ollama!!!)

目录 1、创建虚拟环境2、激活虚拟环境3、安装相关库4、编写脚本&#xff08;test.py&#xff09;调用脚本5、bash中测试通信完美结果 1、创建虚拟环境 conda create -n myenv python3.12 -y2、激活虚拟环境 conda activate myenv3、安装相关库 pip install vllm fastapi uvi…

算力网络(CFN)在跨校联合科研中的应用:安全性挑战与联邦调度实践

引言&#xff1a;科研协作的算力困境 上海交通大学与麻省理工学院联合开展的高能物理模拟实验&#xff0c;因算力资源分配不均导致部分节点连续72小时处于空转状态。这个典型案例揭示了当前跨机构科研协作的痛点&#xff1a;‌算力资源无法实现安全可信的细粒度共享‌。算力网…

高防IP+CDN组合:电商大促的“双保险”防护方案

引言 电商大促期间&#xff0c;平台流量呈爆发式增长&#xff0c;既要应对瞬时激增的访问量&#xff0c;又要防范黑客趁机发起的DDoS攻击、恶意爬虫等威胁。单一防护手段往往难以兼顾性能与安全&#xff0c;而高防IPCDN组合通过“流量清洗加速分发”的双重机制&#xff0c;为电…

# 构建词汇表:自然语言处理中的关键步骤

构建词汇表&#xff1a;自然语言处理中的关键步骤 在自然语言处理&#xff08;NLP&#xff09;任务中&#xff0c;词汇表&#xff08;Vocabulary&#xff09;是文本数据预处理的核心组件之一。它将文本中的单词或字符映射为数值索引&#xff0c;从而让计算机能够理解和处理语言…

SQL进阶知识:七、数据库设计

今天介绍下关于数据库设计的详细介绍&#xff0c;并结合MySQL数据库提供实际例子。 数据库设计是确保数据库能够高效、安全地存储和管理数据的关键环节。良好的数据库设计可以提高查询性能、减少数据冗余、确保数据完整性&#xff0c;并简化数据维护。以下是关于数据库设计的详…

python如何取消word中的缩进

在python-docx中&#xff0c;取消缩进可以通过将相应的缩进属性设置为None或0来实现。以下是取消不同类型缩进的方法&#xff1a; 取消左缩进 from docx import Documentdoc Document(existing_document.docx)for paragraph in doc.paragraphs:# 取消左缩进paragraph.paragr…

Docker拉取镜像代理配置实践与经验分享

Docker拉取镜像代理配置实践与经验分享 一、背景概述 在企业内网环境中&#xff0c;我们部署了多台用于测试与学习的服务器。近期&#xff0c;接到领导安排&#xff0c;需在其中一台服务器上通过Docker安装n8n应用程序。然而在实际操作过程中&#xff0c;遭遇Docker官方镜像库…

【数字图像处理】立体视觉基础(1)

成像 成像过程&#xff1a;三维空间坐标到二维图像坐标的变换 相机矩阵&#xff1a;建立三维到二维的投影关系 相机的使用步骤&#xff08;模型-视图变换&#xff09;&#xff1a; &#xff08;1&#xff09;视图变换 &#xff08;2&#xff09;模型变换 &#xff08;3&…

实验4:列表与字典应用

目的 &#xff1a;熟练操作组合数据类型。 试验任务&#xff1a; 1. 基础&#xff1a;生日悖论分析。如果一个房间有23人或以上&#xff0c;那么至少有两个人的生日相同的概率大于50%。编写程序&#xff0c;输出在不同随机样本数量下&#xff0c;23 个人中至少两个人生日相同的…

c++之网络编程

网络编程&#xff1a;使得计算机程序能够在网络中发送和接受数据&#xff0c;从而实现分布式系统和网络服务的功能。 作用&#xff1a;使应用程序能够通过网络协议与其他计算机程序进行数据交换 基本概念 套接字&#xff08;socket&#xff09;&#xff1a; 套接字是网络通信…

【Harmony_Bug】forEach + asyncawait 的异步陷阱

一、问题描述 今天在做一个RDB的小项目时&#xff0c;遇到一个问题&#xff0c;因为没报错其实也是不算是BUG&#xff0c;以下描述时我就直接说关键点&#xff0c;其他代码忽略。 我的数据模型初始化有六条数据如图 在持久化层&#xff0c;通过initUserData这个方法执行插入。…

大肠杆菌诱导蛋白时OD600=0.6-0.8添加IPTG的思考-实验操作系列-009

一、为什么用OD600表示菌液浓度&#xff1f; 1. 光密度与吸光值的关系 OD600是指在600纳米波长下的光密度&#xff08;Optical Density&#xff09;&#xff0c;也就是通过细菌悬浮液的光的吸收程度。根据比尔-朗伯定律&#xff0c;光密度与溶液中光学活性物质&#xff08;如…

OpenHarmony - 小型系统内核(LiteOS-A)(十),魔法键使用方法,用户态异常信息说明

OpenHarmony - 小型系统内核&#xff08;LiteOS-A&#xff09;&#xff08;十&#xff09; 十四、魔法键使用方法 使用场景 在系统运行出现无响应等情况时&#xff0c;可以通过魔法键功能确定系统是否被锁中断&#xff08;魔法键也无响应&#xff09;或者查看系统任务运行状态…

CUDA编程之Grid、Block、Thread线程模型

一、线程模型:Grid、Block、Thread概念 ‌1. 层级定义‌ ‌Thread(线程)‌ CUDA中最基本的执行单元,对应GPU的单个CUDA核心(SP)。每个线程独立执行核函数指令,拥有独立的寄存器和局部内存空间‌。 ‌Block(线程块)‌ 由多个线程组成(通常为32的倍数),是逻辑上的并…

实战交易策略 篇十九:君山居士熊市交易策略

文章目录 系列文章熊市三大特征熊市操作思维强势重势,弱势重质抢反弹重要前提和五大原则反弹逃顶操盘其他炒股的至高境界力戒“三进三出”八大心理误区八大戒律股市不败之法系列文章 实战交易策略 篇一:奥利弗瓦莱士短线交易策略 实战交易策略 篇二:杰西利弗莫尔股票大作手…

Flutter IOS 真机 Widget 错误。Widget 安装后系统中没有

错误信息&#xff1a; SendProcessControlEvent:toPid: encountered an error: Error Domaincom.apple.dt.deviceprocesscontrolservice Code8 "Failed to show Widget com.xxx.xxx.ServerStatus error: Error DomainFBSOpenApplicationServiceErrorDomain Code1 "T…