0-1背包 完全背包 + 至多/恰好/至少 + 空间优化 + 常见变形题(实战力扣题)

(一)01背包  

1.回溯三问

  • dfs(i,c) = max(dfs(i-1,c),dfs(i-1,c-w[i])+v[i])
# capacity:背包容量
# w[i]: 第 i 个物品的体积
# v[i]: 第 i 个物品的价值
# 返回:所选物品体积和不超过 capacity 的前提下,所能得到的最大价值和
def zero_one_knapsack(capacity:int,w:List[int],v:List[int]) -> int:n = len(w)@cache #记忆化搜索 def dfs(i,c):if i < 0:return 0if c < w[i]:return dfs(i-1,c)return max(dfs(i-1,c),dfs(i-1,c-w[i])+v[i])return dfs(n-1,capacity)

 2.常见变形


 3.实战力扣题 

494. 目标和 - 力扣(LeetCode)

给你一个非负整数数组 nums 和一个整数 target 。向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。


>>思考和分析: 

  • 正数和:p       
  • 负数和:s-p
  • p-(s-p) = t     
  • 2p=s+t
  • 化简可得: p=(s+t)/2

(1)记忆化搜索

class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:target += sum(nums)if target < 0 or target%2: # 负数 or 奇数return 0 # 方案数为0target //= 2n = len(nums)# 记忆化搜索@cachedef dfs(i,c):if i < 0:return 1 if c==0 else 0if c < nums[i]:return dfs(i-1,c)return dfs(i-1,c)+dfs(i-1,c-nums[i])return dfs(n-1,target)

(2)1:1 翻译成递推

  • dfs(i,c) = dfs(i-1,c) + dfs(i-1,c-w[i])
  • f[i][c] = f[i-1][c] + f[i-1][c-w[i]]

  • f[i+1][c] = f[i][c] + f[i][c-w[i]]

class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:target += sum(nums)if target < 0 or target%2: # 负数 or 奇数return 0 # 方案数为0target //= 2n = len(nums)# 二维dpf = [[0]*(target+1)for _ in range(n+1)]f[0][0] = 1for i,x in enumerate(nums):for c in range(target+1):if c<x:f[i+1][c] = f[i][c]else:f[i+1][c] = f[i][c] + f[i][c-x]return f[n][target]
  • 优化空间

方式一:二维数组优化

  • f[(i+1)%2][c] = f[i%2][c] + f[i%2][c-w[i]]
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:# 二维dpf = [[0]*(target+1)for _ in range(2)]f[0][0] = 1for i,x in enumerate(nums):for c in range(target+1):if c<x:f[(i+1)%2][c] = f[i%2][c]else:f[(i+1)%2][c] = f[i%2][c] + f[i%2][c-x]return f[n%2][target]

方式二:一维数组优化

  • f[i+1][c] = f[i][c] + f[i][c-w[i]]
  • f[c]=f[c]+f[c-w[i]]

class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:target += sum(nums)if target < 0 or target%2: # 负数 or 奇数return 0 # 方案数为0target //= 2n = len(nums)# 一维dpf = [0]*(target+1)f[0] = 1for x in nums:for c in range(target,x-1,-1):f[c] = f[c] + f[c-x]return f[target]

拓展问题思考(O_O)?如果题目改成至多为 target,该怎么修改呢?

  • 「恰好」改成「至多」 
# 恰好
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:.........# 记忆化搜索@cachedef dfs(i,c):if i < 0:return 1 if c==0 else 0 # 边界条件:由于要求是恰好组成。恰好情况:当c=0的时候,才能返回1,表示这是一个合法的方案;if c < nums[i]:return dfs(i-1,c)return dfs(i-1,c)+dfs(i-1,c-nums[i])return dfs(n-1,target)改成如下:# 至多
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:.........# 记忆化搜索@cachedef dfs(i,c):if i < 0:return 1 // 至多情况:不用判断c == 0 的情况if c < nums[i]:return dfs(i-1,c)return dfs(i-1,c)+dfs(i-1,c-nums[i])return dfs(n-1,target)
# 恰好
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:.........# 一维dpf = [0]*(target+1)f[0] = 1for x in nums:for c in range(target,x-1,-1):f[c] = f[c] + f[c-x]return f[target]改成如下:# 至多
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:.........n = len(nums)# 一维dpf = [1]*(target+1) # 把这里全都初始化成1for x in nums:for c in range(target,x-1,-1):f[c] = f[c] + f[c-x]return f[target]
  •  「恰好」改成「至少」
# 恰好
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:.........# 记忆化搜索@cachedef dfs(i,c):if i < 0:return 1 if c==0 else 0 # 边界条件:由于要求是恰好组成。恰好情况:当c=0的时候,才能返回1,表示这是一个合法的方案;if c < nums[i]:return dfs(i-1,c)return dfs(i-1,c)+dfs(i-1,c-nums[i])return dfs(n-1,target)改成如下:# 至少
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:.........# 记忆化搜索@cachedef dfs(i,c):if i < 0:return 1 if c<=0 else 0 // 至少情况:c<=0return dfs(i-1,c)+dfs(i-1,c-nums[i])return dfs(n-1,target)
# 恰好
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:.........# 一维dpf = [0]*(target+1)f[0] = 1for x in nums:for c in range(target,x-1,-1):f[c] = f[c] + f[c-x]return f[target]改成如下:# 至少
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:.........n = len(nums)# 一维dpf = [0]*(target+1)f[0] = 1for x in nums:for c in range(target,-1,-1):f[c] = f[c] + f[max(c-x,0)] # 表示把所有 c<=0 的状态 都记录到 f[0] 里面return f[target]

target=0 的情况下,每个物品都可以 「选」 或者 「不选」,那么一共有 2^{n} 种方案,刚好这种写法可以从 1 开始,算出来  2^{n}(这句话我听得迷迷糊糊的,有小伙伴懂得指导我一下_(:з」∠)_

 (二)完全背包  

1.回溯三问

  • dfs(i,c) = max(dfs(i-1,c),dfs(i,c-w[i])+v[i])
# capacity:背包容量
# w[i]: 第 i 个物品的体积
# v[i]: 第 i 个物品的价值
# 每种物品可以无限次重复选
# 返回:所选物品体积和不超过 capacity 的前提下,所能得到的最大价值和
def unbounded_knapsack(capacity:int,w:List[int],v:List[int]) -> int:n = len(w)@cachedef dfs(i,c):if i < 0:return 0if c < w[i]:return dfs(i-1,c)return max(dfs(i-1,c),dfs(i,c-w[i])+v[i])return dfs(n-1,capacity)

(1)01背包完全背包递归式对比

  • dfs(i,c) = max(dfs(i-1,c),dfs(i-1,c-w[i])+v[i])
  • dfs(i,c) = max(dfs(i-1,c),dfs(i,c-w[i])+v[i])

【重点】在选了一个物品之后i 是不变的,表示你可以继续选 i 种物品,所以这里不是递归到 i-1,而是 i


2.常见变形


3.实战力扣题 

322. 零钱兑换 - 力扣(LeetCode)

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。你可以认为每种硬币的数量是无限的


(1)记忆化搜索

class Solution:# 边界条件:由于要求是恰好组成 amount,所以和上一题一样# 当c=0的时候,才能返回0,表示这是一个合法的方案;否则就应该返回无穷大,表示这是一个不合法的方案# 这样后面去min的时候就自然的取到了不是无穷大的那个方案(硬币个数)# 如果ans小于inf,表示这是一个合法的方案,否则一个合法的方案都没有def coinChange(self, coins: List[int], amount: int) -> int:n = len(coins)@cachedef dfs(i,c):if i < 0:return 0 if c==0 else infif c < coins[i]:return dfs(i-1,c)return min(dfs(i-1,c),dfs(i,c-coins[i])+1)ans = dfs(n-1,amount)return ans if ans<inf else -1

(2)1:1 翻译成递推

  • dfs(i,c) = min(dfs(i-1,c),dfs(i,c-w[i])+v[i])
  • f[i][c] = min(f[i-1][c],f[i][c-w[i]]+v[i])

  • f[i+1][c] = min(f[i][c],f[i+1][c-w[i]]+v[i])

class Solution:def coinChange(self, coins: List[int], amount: int) -> int:n = len(coins)# 数组的初始值:根据递归的边界条件,可知当i=0,c=0,才是0;其它的都是无穷大# 步骤:1:直接把数组初始化成无穷大 2.把f[0][0]改成0就好了f=[[inf] * (amount+1) for _ in range(n+1)]f[0][0] = 0for i,x in enumerate(coins):for c in range(amount+1):# 强调一下,c 表示剩余容量if c<x:f[i+1][c] = f[i][c]else:f[i+1][c] = min(f[i][c],f[i+1][c-x]+1)ans = f[n][amount]return ans if ans < inf else -1
  • 优化空间

方式一:二维数组优化

  • f[(i+1)%2][c] = min(f[i%2][c],f[(i+1)%2][c-w[i]]+v[i])
class Solution:def coinChange(self, coins: List[int], amount: int) -> int:n = len(coins)# 数组的初始值:根据递归的边界条件,可知当i=0,c=0,才是0;其它的都是无穷大# 步骤:1:直接把数组初始化成无穷大 2.把f[0][0]改成0就好了f=[[inf] * (amount+1) for _ in range(2)]f[0][0] = 0for i,x in enumerate(coins):for c in range(amount+1):# 强调一下,c 表示剩余容量if c<x:f[(i+1)%2][c] = f[i%2][c]else:f[(i+1)%2][c] = min(f[i%2][c],f[(i+1)%2][c-x]+1)ans = f[n%2][amount]return ans if ans < inf else -1

方式二:一维数组优化

  • f[i+1][c] = min(f[i][c],f[i+1][c-w[i]]+v[i])
  • f[c] = min(f[c],f[c-w[i]]+v[i])

  • 需要注意:在恰好装满的情况下,这个数组它不一定是个有序数组 
class Solution:def coinChange(self, coins: List[int], amount: int) -> int:n = len(coins)f=[inf] * (amount+1) f[0] = 0for x in coins:for c in range(x,amount+1):f[c] = min(f[c],f[c-x]+1)ans = f[amount]return ans if ans < inf else -1

总结一下循环顺序问题(O_O)?

  • 如果你在写一个不是背包的DP问题,但是得到了类似的状态转移方程,那么要怎么思考循环的顺序呢?

实战篇:这篇文章就是得到了类似的状态转移方程,用上图来思考循环顺序

LCR 166.珠宝的最高价值 + 动态规划 + 记忆化搜索 + 递推 + 空间优化-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/134207574?spm=1001.2014.3001.5501

参考和推荐视频:

0-1背包 完全背包【基础算法精讲 18】_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV16Y411v7Y6/?spm_id_from=333.788&vd_source=a934d7fc6f47698a29dac90a922ba5a3

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

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

相关文章

【Linux】第八站:gcc和g++的使用

文章目录 一、解决sudo命令的问题二、Linux编译器-gcc/g1.gcc的使用2.g的使用 三、gcc编译链接过程1.预处理2.编译&#xff08;生成汇编&#xff09;3.汇编&#xff08;生成机器可识别代码&#xff09;4.链接&#xff08;生成可执行文件或库文件&#xff09;5.一些选项的意义 四…

Docker容器技术实战3

8、docker原生网络 Docker原生网络基于Linux桥接技术和虚拟网络接口&#xff0c;使用了Linux内核的网络功能。每个Docker容器都有自己的网络命名空间&#xff0c;这使得容器之间可以使用独立的IP地址&#xff0c;并隔离了容器的网络栈。 当创建一个Docker原生网络时&#xff…

Airtest工具根据App页面文字信息提取坐标进行截图保存在自定义文件夹

Airtest工具根据App页面文字信息提取坐标进行截图保存在自定义文件夹 一、项目背景 在一个项目中&#xff0c;选项被选中和未选中的节点元素的属性值无变化&#xff0c;通过AI识别率达不到百分百&#xff0c;想着通过计算图片的HSV值来判断选择能否被选中。&#xff08;HSV比…

ESP32 for Arduino 分区信息

忘记过去&#xff0c;超越自己 ❤️ 博客主页 单片机菜鸟哥&#xff0c;一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-11-04❤️❤️ 本篇更新记录 2023-11-04❤️&#x1f389; 欢迎关注 &#x1f50e;点赞 &#x1f44d;收藏 ⭐️留言&#x1f4dd;&#x1f64f;…

【JAVA学习笔记】59 - JUnit框架使用、本章作业

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter15/src/com/yinhai/homework JUnit测试框架 1.基本介绍 1. JUnit是一个Java语言的单元测试框架 2.多数Java的开发环境都已经集成了JUnit作为单元测试的工具 2.如何使用 创建方法后&#x…

JVM离线分析-使用MAT分析dump堆文件

1. MAT&#xff08;Memory Analyzer Tool&#xff09;的介绍 官方介绍 The Eclipse Memory Analyzer is a fast and feature-rich Java heap analyzer that helps you find memory leaks and reduce memory consumption. Use the Memory Analyzer to analyze productive heap …

Java随机获取某个范围内的随机整数

随机获取某个范围内的随机整数 一、代码 /*** 随机获取某个范围内的随机整数的值* param min 最小值* param max 最大值* return*/public static int randomNum(int min,int max) {// 创建一个Random对象Random random new Random();// 生成指定范围内的随机整数int randomI…

通过@ConfigrationProperties读取配置文件属性并赋值

这种设计使得 Anything 成为 Something 类的静态成员&#xff0c;因此不依赖于外部类的实例。静态内部类通常更独立&#xff0c;它们可以单独存在且无需引用外部类的实例。 如果将 Anything 类定义为非静态的内部类&#xff0c;它将依赖于 Something 类的实例。这意味着每个 S…

vue中的rules表单校验规则使用方法 :rules=“rules“

一、el-form里面必写属性值 :ref"dataForm" // 提交表单时进行校验 :rules"rules" // return 下的校验规则 :model"userForm" // 绑定表单的值 <el-formref"dataForm" // 必写属性值:rules"rules"…

CV论文阅读大合集

YearNameAreamodeldescriptiondrawback2021 ICMLClip &#xff08;Contrastive Language-Image Pre-training&#xff09;contrastive learning、zero-shot learing、mutimodel用文本作为监督信号来训练可迁移的视觉模型CLIP’s zero-shot performance, although comparable to…

redis缓存击穿,redisson分布式锁,redis逻辑过期

什么是缓存击穿&#xff1a; 缓存击穿是指在高并发环境下&#xff0c;某个热点数据的缓存过期&#xff0c;导致大量请求同时访问后端存储系统&#xff0c;引起系统性能下降和后端存储压力过大的现象。 解决方案&#xff1a; 1. redisson分布式锁 本质上是缓存重建的过程中&…

「Verilog学习笔记」四选一多路器

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 分析 通过波形示意图我们可以发现&#xff0c;当sel为0&#xff0c;1&#xff0c;2时&#xff0c;输出mux_out分别为d3&#xff0c;d2&#xff0c;d1&#xff0c;那么sel3…

PHP中文转拼音实现

pinyin.php 床前明月光&#xff0c;疑是地上霜。 举头望明月&#xff0c;低头思故乡 <?php /*** PHP 汉字转拼音 [包含20902个基本汉字5059生僻字]* author 楼教主(cik520qq.com)* version v1.2* note 请开启 mb_string 扩展*/var_dump(pinyin(床前明月光&#xff0c;疑是…

【Redis】的简介和安装配置(Linux和windows)及操作命令

目录 一、概述 1.介绍 2.特点 二、安配 1. 安装 2. 配置 3. 主机连接 1.Linux连接 2.windows连接 三、命令 1. 字符串(String) 2. 哈希(Hash) 3. 列表&#xff08;List&#xff09; 4. 集合&#xff08;Set&#xff09; 一、概述 1.介绍 Redis是一个开源的、基…

零代码复现-TCGA联合GEO免疫基因结合代谢基因生信套路(二)

零代码复现-TCGA联合GEO免疫基因结合代谢基因生信套路&#xff08;二&#xff09;-关键基因集的获取和生存数据准备 前面的分析中&#xff0c;下载TCGA和GEO的数据&#xff0c;并进行简单的处理&#xff0c;接下来就是相关基因集的获取和整理&#xff0c;为后期聚类和降维做准…

【PyQt学习篇 · ⑥】:QWidget - 事件

文章目录 事件消息显示和关闭事件移动事件调整大小事件鼠标事件进入和离开事件鼠标按下和释放事件鼠标双击事件鼠标按下移动事件 键盘事件焦点事件拖拽事件绘制事件改变事件右键菜单输入法 事件转发机制案例一案例二案例三 事件消息 显示和关闭事件 showEvent(QShowEvent)方法…

小白如何制作电子画册?看这里,超多画册模板任你挑!

传统纸质版的画册&#xff0c;制作起来即费力又费时&#xff0c;花费还高&#xff0c;想要修改内容还得重新制作&#xff0c;特别麻烦。现在互联网发达&#xff0c;如今已经用上了H5的技术&#xff0c;小白也能快速制作一本翻页电子画册。 只需用FLBOOK&#xff0c;在线就可以制…

Linux的历史与环境

目录 Linux的背景介绍 Linux的时代背景-硅谷模式 计算机发展 UNIX发展历史 Linux诞生的偶然与必然 Linux开源 Linux发行版本 搭建Linux的环境 1.直接安装在物理机上 2.使用虚拟机软件 3.使用云服务器 &#xff08;1&#xff09;购买云服务器 &#xff08;2&#x…

OceanBase:03-集群部署

目录 一、集群规划 二、配置要求 三、部署前配置 1.配置 limits.conf 2.配置 sysctl.conf 3.关闭防火墙 4.关闭 SELinux 5.创建数据目录&#xff0c;修改文件所有者信息 6.设置无密码 SSH 登录 7.安装jdk 四、解压执行安装 五、集群部署 1.OBD命令行部署 2. OBD白…

2019年408真题复盘

紫色标记是认为有一定的思维难度或重点总结 红色标记是这次刷真题做错的 记录自己对题目的一些想法与联系&#xff0c;可能并不太关注题目本身。 分数用时 选择部分 80/8036min大题部分41/7094min总分121130min 摘自知乎老哥&#xff1a;“我做历年真题时&#xff0c;绝大部分…