Leetcode刷题笔记——动态规划(背包问题)篇

Leetcode刷题笔记——动态规划(背包问题)篇

一、0-1 背包问题

0-1背包问题简介

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

在这里插入图片描述
1. 确定 dp 数组(dp table)以及下标的含义
dp[i][j]:从下标为 [0-i] 的物品里任意取,放进容量为 j的背包,价值总和最大是多少

2. 确定递推公式

case1: 不放物品 i:由 dp[i - 1][j] 推出,即背包容量为 j,里面不放物品 i 的最大价值

case2: 放物品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])

3. dp 数组如何初始化
在这里插入图片描述
dp[0][j] 即:使用各个容量的背包存放编号 0 的物品所能获得的最大价值,即第一行的初始化
dp[i][0] 即:背包容量为 0 的时候,存放各个物品编号所能获得的最大价值,即第一列的初始化

其实从递推公式可以看出 dp[i][j] 是由左上方数值推导出来了,那么 其他下标初始为什么数值都可以,因为都会被覆盖,这里统一把dp数组统一初始为0,更方便一些
在这里插入图片描述

4. 确定遍历顺序
可以看出,有两个遍历的维度:物品与背包重量

5. 打印 dp 数组

python代码解法

class Solution:def BagProblem(self, weight: List[int], value: List[int], bag_capacity: int) -> int:""":param weight: 物品重量:param value: 物品价值:param bag_capacity: 背包容量:return: 背包所能装的最大价值"""nums = len(weight)dp = [[0] * (bag_capacity + 1) for _ in range(nums)]for i in range(0, bag_capacity + 1):if i >= weight[0]:dp[0][i] = value[0]for i in range(1, nums):for capacity in range(1, bag_capacity + 1):# 不放物品i: dp[i - 1][capacity]:背包容量为 capacity 不放物品i的最大价值# 放物品i: dp[i - 1][capacity - weight[i]] + value[i] 的最大价值if capacity >= weight[i]:   # 当背包容量大于等于物品重量的时候通过状态转移方程计算最大价值dp[i][capacity] = max(dp[i - 1][capacity],dp[i - 1][capacity - weight[i]] + value[i])else:dp[i][capacity] = dp[i - 1][capacity]print(dp)return dp[nums - 1][bag_capacity]if __name__ == '__main__':s = Solution()weight = [1, 3, 4]  # 物品重量weight.sort()value = [15, 20, 30]  # 物品价值value.sort()bag_capacity = 4print(s.BagProblem(weight, value, bag_capacity))

0-1背包问题的优化
对于背包问题其实状态都是可以压缩的
1. 确定 dp 数组(dp table)以及下标的含义
在一维 dp 数组中,dp[j] 表示:容量为 j 的背包,所背的物品价值可以最大为 dp[j]

2. 确定递推公式
dp[j] 为容量为 j 的背包所背的最大价值,那么如何推导 dp[j] 呢?

dp[j] 有两个选择:
case1:不放物品 i,取自己 dp[j] 相当于 二维 dp 数组中的 dp[i-1][j]

case2:放物品 i,取 dp[j - weight[i]] + value[i],放物品 i,指定是取最大的,毕竟是求最大价值

3. dp 数组如何初始化
dp[j] 表示:容量为 j 的背包,所背的物品价值可以最大为 dp[j],那么 dp[0] 就应该是 0,因为背包容量为 0 所背的物品的最大价值就是 0

物品0 的重量 weight[0] = 1,价值 value[0] = 15
正序遍历
dp[1] = dp[1 - weight[0]] + value[0] = 15

dp[2] = dp[2 - weight[0]] + value[0] = 30

此时 dp[2] 就已经是 30 了,意味着物品 0,被放入了两次

倒序就是先算dp[2]

dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)

dp[1] = dp[1 - weight[0]] + value[0] = 15

从后往前循环,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了

4.一维dp数组遍历顺序
二维 dp 遍历的时候,背包容量是从小到大,而一维 dp 遍历的时候,背包是从大到小,
倒序遍历是为了保证物品 i 只被放入一次!,一旦正序遍历了,那么物品0就会被重复加入多次!

weight = [1, 3, 4]      # 物品重量
value = [15, 20, 30]    # 物品价值bag_capacity = 4        # 背包容量
dp = [0] * (bag_capacity + 1)   # 表示容量为j的的背包所能装的最大价值为dp[j]for i in range(0, len(weight)):     # 遍历物品for capacity in range(bag_capacity, weight[i] - 1, -1):     # 倒序遍历背包容量dp[capacity] = max(dp[capacity], dp[capacity - weight[i]] + value[i])print(f"倒叙遍历背包容量至物品{i}的重量,在每个背包容量下所能装载的最大价值为:{dp}(每个物品只能装一次)")# 倒叙遍历背包容量至物品 0 的重量,在每个背包容量下所能装载的最大价值为:[0, 15, 15, 15, 15](每个物品只能装一次)
# 倒叙遍历背包容量至物品 1 的重量,在每个背包容量下所能装载的最大价值为:[0, 15, 15, 20, 35](每个物品只能装一次)
# 倒叙遍历背包容量至物品 2 的重量,在每个背包容量下所能装载的最大价值为:[0, 15, 15, 20, 35](每个物品只能装一次)

5. 打印 dp 数组

在这里插入图片描述

0-1背包问题力扣实战练习

第一题

Leetcode416:分割等和子集:中等题 (详情点击链接见原题)

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

这道题目是要找是否可以将这个数组分割成两个子集使得两个子集的元素和相等,那么只要找到集合里能够出现 sum / 2 的子集总和就算是可以分割

  • 背包的体积为sum / 2
  • 背包要放入的物品(集合里的元素)集合里的元素代表物品的重量和价值
  • 背包如果正好装满说明找到了总和为sum / 2 的子集
  • 背包中的每一个元素是不可重复放入的

1. 确定 dp 数组(dp table)以及下标的含义
dp[i] 表示背包总容量为i,放进物品后所背的最大重量为dp[i]
那么如果背包容量为 targetdp[target] 就是装满 背包之后的重量,所以 当 dp[target] == target 的时候,背包就装满了

2.确定递推公式
本题相当于往背包里放入数值
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])

3. dp 数组如何初始化
dp[0] = 0,如果题目给的元素都是正整数那么非 0 下标都初始化为 0 就可以了,如果题目所给的元素有负数,那么非 0 下标应初始化为 -∞这样才能让 dp 数组在递推过程中取得最大的价值,而不是被初始值覆盖了

# 题目说每个数组中的元素不会超过100,数组的大小不会超过200,总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了
dp = [0] * 10001

4.确定遍历顺序:如果使用一维 dp 数组,物品遍历的 for 循环放在外层,遍历背包的 for 循环放在内层,且内层 for 循环倒序遍历

python代码解法

class Solution:def canPartition(self, nums: List[int]) -> bool:if sum(nums) % 2 != 0:return Falsenums.sort()target = sum(nums) // 2# 题目说每个数组中的元素不会超过100,数组的大小不会超过200# 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了dp = [0] * 10001for i in range(0, len(nums)):       # 遍历物品(nums中的元素)# 遍历背包容量, 每一个元素一定是不可重复放入,所以从大到小遍历for j in range(target, nums[i] - 1, -1):dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])# 集合中的元素正好可以装满容量为target的背包if dp[target] == target:return Truereturn False

二、完全背包问题

1.完全背包问题简介

N 件物品和一个最多能背重量为 W 的背包。第i件物品的重量是 weight[i],得到的价值是 value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大

完全背包01背包问题唯一不同的地方就是,每种物品有无限件

weight = [1, 3, 4]      # 物品重量
value = [15, 20, 30]    # 物品价值bag_capacity = 4        # 背包容量
dp = [0] * (bag_capacity + 1)   # 表示容量为j的的背包所能装的最大价值为dp[j]for i in range(0, len(weight)):     # 遍历物品for capacity in range(weight[i], bag_capacity + 1):     # 倒序遍历背包容量dp[capacity] = max(dp[capacity], dp[capacity - weight[i]] + value[i])print(f"遍历物品{i}的重量至背包容量,在每个背包容量下所能装载的最大价值为:{dp}(每个物品可以装无限次)")# 遍历物品 0 的重量至背包容量,在每个背包容量下所能装载的最大价值为:[0, 15, 30, 45, 60](每个物品可以装无限次)
# 遍历物品 1 的重量至背包容量,在每个背包容量下所能装载的最大价值为:[0, 15, 30, 45, 60](每个物品可以装无限次)
# 遍历物品 2 的重量至背包容量,在每个背包容量下所能装载的最大价值为:[0, 15, 30, 45, 60](每个物品可以装无限次)
重量价值
物品0115
物品1320
物品2430

01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次

2.完全背包问题力扣实战练习

第一题

Leetcode322:零钱兑换:中等题 (详情点击链接见原题)

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额
计算并返回可以凑成总金额所需的 最少的硬币个数

1. 确定 dp 数组(dp table)以及下标的含义
dp[j]:凑足总额为j所需钱币的最少个数为 dp[j]

2.确定递推公式
凑足总额为 j - coins[i] 的最少个数为 dp[j - coins[i]],那么只需要加上一个钱币 coins[i]dp[j - coins[i]] + 1就是 dp[j](考虑 coins[i]

递推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);

3. dp 数组如何初始化

4. 确定遍历顺序

python代码解法

class Solution:def coinChange(self, coins: List[int], amount: int) -> int:dp = [float('inf')] * (amount + 1)  # dp[j]:表示凑成总金额为j的最少硬币个数,初始化为最大值dp[0] = 0for coin in coins:  # 遍历硬币(遍历物品)for j in range(coin, amount + 1):    # 遍历金额(遍历背包容量)dp[j] = min(dp[j], dp[j - coin] + 1)return dp[amount] if dp[amount] != float('inf') else -1

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

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

相关文章

Unload-labs

function checkFile() {var file document.getElementsByName(upload_file)[0].value;if (file null || file "") {alert("请选择要上传的文件!");return false;}//定义允许上传的文件类型var allow_ext ".jpg|.png|.gif";//提取上传文件的类…

代码算法训练营day9 | 28. 实现 strStr() 、459.重复的子字符串

day9: 28. 实现 strStr()KMP的主要应用:什么是前缀表:前缀表是如何记录的: 如何计算前缀表:构造next数组:1、初始化2、处理前后缀不相同的情况3、处理前后缀相同的情况 代码: 459.重复的子字符串…

Mysql 索引、锁与MVCC等相关知识点

文章目录 Mysql锁的类型锁使用MVCC快照读和当前读读视图【Read View】串行化的解决 索引类型存储方式区分逻辑区分实际使用区分索引失效情况 索引建立规范SQL编写规范exlpain字段解析ACID的原理日志引擎慢SQL整合SpringBoot博客记录 Mysql锁的类型 MySQL中有哪些锁&#xff1a…

德人合科技 | 公司办公终端、电脑文件资料 \ 数据透明加密防泄密管理软件系统

天锐绿盾是一款全面的企业级数据安全解决方案,它专注于为企业办公终端、电脑文件资料提供数据透明加密防泄密管理。 首页 德人合科技——www.drhchina.com 这款软件系统的主要功能特点包括: 1. **透明加密技术**: 天锐绿盾采用了透明加密技…

HarmonyOS NEXT应用开发—Grid和List内拖拽交换子组件位置

介绍 本示例分别通过onItemDrop()和onDrop()回调,实现子组件在Grid和List中的子组件位置交换。 效果图预览 使用说明: 拖拽Grid中子组件,到目标Grid子组件位置,进行两者位置互换。拖拽List中子组件,到目标List子组件…

ChromeDriver 122 版本为例 国内下载地址及安装教程

ChromeDriver 国内下载地址 https://chromedriver.com/download 靠谱 千千万万别下载错了 先确认 Chrome 浏览器版本 以 win64 版本为例 那我们下载这一个啊,不要下载错了 下载地址贴在这哈 https://storage.googleapis.com/chrome-for-testing-public/122.0.…

HarmonyOS鸿蒙开发常用4种布局详细说明

介绍一下鸿蒙开发常用4种布局 1、线性布局 2、层叠布局 3、网格布局 4、列表布局 ​1. 线性布局(Column/Row) 线性布局(LinearLayout)是开发中最常用的布局,通过线性容器Row(行)和Column&…

SpringSecurity(SpringBoot2.X版本实现)

资料来源于 SpringSecurity框架教程-Spring SecurityJWT实现项目级前端分离认证授权 侵权删 目录 介绍 快速开始 认证 认证流程 登录校验流程 SpringSecurity完整流程 认证流程详解 代码实现 准备工作 mysql mybatis-plus redis 统一返回类 核心代码 密码加密存…

神策分析 Copilot 成功通过网信办算法备案,数据分析 AI 化全面落地

近日,神策数据严格遵循《互联网信息服务深度合成管理规定》,已完成智能数据问答算法备案。该算法基于大模型技术,专注于为客户提供数据指标查询和数据洞察方面的专业回答。 神策分析 Copilot 运用神策数据智能数据问答算法,聚焦分…

Vue-router3.0版本跳转报错

1.路由创建之后发现控制台push路由跳转报错了 2.解决方法: //在router文件中添加 const originalPush VueRouter.prototype.push VueRouter.prototype.push function push(location) {return originalPush.call(this, location).catch(err > err) }3.解决了

【深度学习模型移植】用torch普通算子组合替代torch.einsum方法

首先不得不佩服大模型的强大之处,在算法移植过程中遇到einsum算子在ONNX中不支持,因此需要使用普通算子替代。参考TensorRT - 使用torch普通算子组合替代torch.einsum爱因斯坦求和约定算子的一般性方法。可以写出简单的替换方法,但是该方法会…

有上百个文件夹需要按顺序编码重命名怎么办?这个方法值得你收藏

在日常生活和工作中,我们经常需要管理大量的文件夹,以便更好地组织和存储文件。为了更方便地查找和识别文件夹,给文件夹按号码命名是一种非常实用的方法。下面,我将详细介绍如何给文件夹按号码命名,并提供一些实用的建…

[Python初阶]2255.统计是给定字符串前缀的字符串数目

目录 2255.统计是给定字符串前缀的字符串数目 ①.题目 ②.问题分析 ③.startswith()方法理解 与 说明 Ⅰ.定义和用法 Ⅱ.语法 ④.问题解决 ⑤总结 2255.统计是给定字符串前缀的字符串数目 ①.题目 ②.问题分析 需求:统计列表words中,是字符串s的前缀的字符串的数目. 解…

【pynput】监控是否打开百度贴吧网页

文章目录 简介Demo 简介 有网友提过一个要求,用 Python 实现一个 电脑打开某网站就自动关机的功能。 想到的思路有两个: 【windows 平台】, 获取活动的窗口标题,如果标题里包含了某些网站名称, 那就使用关机命令 可以定时拉取标题, 也可以使…

Python 导入Excel三维坐标数据 生成三维曲面地形图(体) 5-3、线条平滑曲面且可通过面观察柱体变化(三)

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…

【计算机网络_应用层】https协议——加密和窃密的攻防

文章目录 1.https协议的介绍2. 加密和解密2.1 什么是加密2.2 常见的加密方式2.2.1 对称加密2.2.2 非对称加密 2.3 数据摘要(数据指纹)2.4 数字签名 3. https协议的加密和解密方案一:使用对称加密(❌)方案二&#xff1a…

嵌入式学习39-程序创建数据库及查找

1.sqlite3_open int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); 功能: 打开 数据库文件(创建一个数据库连接) 参数: filename: …

react-面试题

一、组件基础 1. React 事件机制 <div onClick{this.handleClick.bind(this)}>点我</div> React并不是将click事件绑定到了div的真实DOM上&#xff0c;而是在document处监听了所有的事件&#xff0c;当事件发生并且冒泡到document处的时候&#xff0c;React将事…

小蓝的漆房——算法思路

题目链接&#xff1a;1.小蓝的漆房 - 蓝桥云课 (lanqiao.cn) 本题只要是通过枚举的方法&#xff0c;算出涂成每一种颜色所需的天数&#xff0c;最后在所有天数中找出最小值&#xff08;由题可知&#xff0c;最多只有60种颜色&#xff0c;所以可以尝试算出每种颜色所需的时间&am…

Linux网络编程: IP协议详解

一、TCP/IP五层模型 物理层&#xff08;Physical Layer&#xff09;&#xff1a;物理层是最底层&#xff0c;负责传输比特流&#xff08;bitstream&#xff09;以及物理介质的传输方式。它定义了如何在物理媒介上传输原始的比特流&#xff0c;例如通过电缆、光纤或无线传输等。…