动态规划 - 背包问题 - 01背包

 01背包问题

二维数组

1. 确定dp数组(dp table)以及下标的含义:dp[i][j]-下标为[0,i]的物品,任取放容量为j的背包中的最大价值
2. 确定递推公式:dp[i][j] = max(dp[i-1][j](不放物品i), dp[i-1][j-weight[i]]+value[i](放物品i))
3. dp数组如何初始化: dp[i][0]=0,dp[0][j]很复杂
4. 确定遍历顺序:二维数组实现的01背包,物品和背包遍历先后顺序无所谓

一维数组--滚动数组--覆盖,把上一层数组拷贝下来

1. 确定dp数组(dp table)以及下标的含义:dp[j]-容量为j的背包所背的最大价值
2. 确定递推公式:dp[j]= max(dp[j], dp[j-weight[i]]+value[i])
3. dp数组如何初始化:dp[0] = 0
4. 确定遍历顺序:先物品再背包,背包需要倒序

常见变形

  • 至多装xx容量,求方案数/最大价值和
  • 恰好装xx容量,求方案数/最大/最小价值和
  • 至少装xx容量,求方案数/最小价值和
@cache  # 缓存装饰器,避免重复计算 dfs 的结果(记忆化)
def dfs(i: int, c: int) -> int:
    if i < 0:
        return 0
    if c < w[i]:
        return dfs(i - 1, c)  # 只能不选
    return max(dfs(i - 1, c), dfs(i - 1, c - x)  # 不选 + 选
return dfs(len(nums) - 1, m)

1:1 翻译成递推

        n = len(nums)
        f = [[0] * (m + 1) for _ in range(n + 1)] #m:背包容量
        f[0][0] = 1
        for i, x in enumerate(nums):
            for c in range(m + 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][m]

优化成一维

n = len(nums)
dp = [0]*(target+1)
dp[0] = 1
for i, x in enumerate(nums):
    for c in range(target+1, x-1, -1):
        dp[c] = max(dp[c], dp[c - x])  
return dp[target]

416. 分割等和子集: 能否装满  max() target == dp[target]

中等

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

看是否可以分成两个子集,和都为总和/2,每个元素的值即是重量又是价值

抽象成背包问题:看容量为总和/2的背包是否能装满, 每个元素只能使用一次:0-1背包

解题步骤:
1. 确定dp数组(dp table)以及下标的含义:是否可以通过选择一些元素来构成和为 j 的子集,容量为j的背包能背的最大价值是否为j。dp[target]== target, dp[sum/2] == sum/2
2. 确定递推公式:dp[j]=max(dp[j],dp[j-weight[i]]+value[i])

                             dp[j]=max(dp[j],dp[j-nums[i]]+nums[i])
3. dp数组如何初始化:dp[0]=0, 
4. 确定遍历顺序:先物品再背包,背包需要倒序

代码随想录:时间只打败20% 

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        n = len(nums)
        s = sum(nums)
        if s%2 == 1: return False #总和除2有余数,不可能分成两个和一样的子集
        target = s //2
        dp=[0]*(target+1) #覆盖从 0 到 target 的所有可能和
        for i,x in enumerate(nums):#物品 nums[i] = x
            for j in range(target,x-1,-1):
# 从后向前遍历(target到nums[i]),确保每个物品只被使用一次
                dp [j] = max(dp[j],dp[j-x]+x)
        return target == dp[target]

考虑 nums[i] 选或不选:

选:问题变成能否从 nums[0] 到 nums[i−1] 中选出一个和恰好等于 j−nums[i] 的子序列,即 dfs(i−1,j−nums[i])。

不选:问题变成能否从 nums[0] 到 nums[i−1] 中选出一个和恰好等于 j 的子序列,即 dfs(i−1,j)。

 f[i + 1][j] = j >= x and f[i][j - x] or f[i][j]
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        total = sum(nums)
        if total % 2 == 1: return False #和为奇数,不可能分成两个和一样的
        target = total // 2
        dp = [True]+[False] * target #dp[0]=true
        for i,x in enumerate(nums): #枚举物品
            for j in range(target, x - 1, -1): #背包
                dp[j] = dp[j] or dp[j - x] 
        return dp[target]

1049. 最后一块石头的重量 II:能装的最大价值: max ()

中等

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

把石头分为两堆,使两堆的重量最接近,即尽量找到sum//2的一堆。那么问题就转化为了在石头堆中找到重量和为sum//2的一部分元素。

石头的重量即价值

1. 确定dp数组(dp table)以及下标的含义:dp[j] -装满容量为j的背包的最大重量dp[j]
2. 确定递推公式: dp[j] = max(dp[j], dp[j-stone[i]]+stone[i])
3. dp数组如何初始化:dp[0] = 0 
4. 确定遍历顺序: 物品正序,背包倒序

注意返回值

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        s = sum(stones)
        target = s//2
        dp = [0]*(target+1)
        for i,x in enumerate(stones): #stone[i] =x
            for j in range(target, x-1, -1):
                dp[j] = max (dp[j],dp[j-x]+x)
        return  s - 2*dp[target]
    #target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的。
    #那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。

494. 目标和:有多少种方式能装满:dp[j] = dp[j]+dp[j-x]

中等

给你一个非负整数数组 nums 和一个整数 target 。

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

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

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

正数p,负数部分的绝对值:s-p,则题目转化成s-(s-p)=target的方案数,p =(target+s)/2。装满容量为(target+s)/2的背包有多少种方法

注意点:s+target必须是偶数,nums[i]不为负--s(不为负)+target 需不为负

1. 确定dp数组(dp table)以及下标的含义:装满容量为j的背包有dp[j]种方法
2. 确定递推公式: dp[j] = dp[j]+dp[j-x]
3. dp数组如何初始化: dp[0]=1
4. 确定遍历顺序:0-1背包的遍历顺序

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        s = sum(nums)
        p = s+target
        if p<0 or p%2: #s+target必须是偶数,nums[i]为正--s+target需不为负
            return 0
        p //=2
        dp = [0]*(p+1)
        dp[0] = 1
        for i,x in enumerate(nums):
            for j in range(p,x-1,-1):
                dp[j] = dp[j]+dp[j-x]
        return dp[p]

474. 一和零:背包最多物品个数

中等

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

 还是0-1背包问题:m n 可以理解为背包的两个维度(有两个重量约束,完全背包是一个物品可以放多次),求至多装的物品个数

"10"、"0001"、"111001"都是物品,只可以被放一次,所以还是0-1背包。

1. 确定dp数组(dp table)以及下标的含义:最多装i个0和j个1的背包的最大物品个数
2. 确定递推公式: dp[i][j] = max(dp[i-x][j-y]+1,dp[i][j])(物品有x个0,y个1,放和不放)
3. dp数组如何初始化: dp[0][0]=0
4. 确定遍历顺序:0-1背包的遍历顺序。物品正序,背包倒序

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        dp = [[0 for _ in range((n+1))]for _ in range(m+1)]
        # 物品
        for s in strs:
            cnt_0 = s.count("0") # 统计字符串中0的个数
            cnt_1 = s.count("1") # 统计字符串中1的个数
            # 背包
            for i in range(m, cnt_0-1, -1):
                for j in range(n, cnt_1-1,-1):
                    dp[i][j] = max(dp[i-cnt_0][j-cnt_1]+1, dp[i][j])
        return dp[m][n]    

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

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

相关文章

研发效能DevOps: Vite 使用 Vue Router

目录 一、实验 1.环境 2.初始化前端项目 3.安装vue-router 4.Vite 使用 Vue Router 二、问题 1.运行出现空页面 2.Vue Router如何禁止页面回退 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统 软件版本备注Windows11VS Code1.94.2Node.jsv18.20.4(LT…

Redis 篇-深入了解在 Linux 的 Redis 网络模型结构及其流程(阻塞 IO、非阻塞 IO、IO 多路复用、异步 IO、信号驱动 IO)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 用户空间与内核空间概述 2.0 Redis 网络模型 2.1 Redis 网络模型 - 阻塞 IO 2.2 Redis 网络模型 - 非阻塞 IO 2.3 Redis 网络模型 - IO 多路复用 2.3.1 IO 多路复…

WPF LiveChart控件基础属性介绍

WPF LiveChart控件基础属性介绍 在Nuget添加方法如下&#xff1a; 然后在xaml中添加引用&#xff1a; xmlns:lvc"clr-namespace:LiveCharts.Wpf;assemblyLiveCharts.Wpf"调用控件&#xff1a; <lvc:CartesianChart Name"chart" Margin"40"…

Java应用程序的服务器有哪些

1.Tomcat、Jetty 和 JBoss 区别&#xff1f; Apache Tomcat、Jetty 和 JBoss都是用于部署Java应用程序的服务器&#xff0c;它们都支持Servlet、JSP和其他Java EE&#xff08;现在称为Jakarta EE&#xff09;技术。尽管它们有一些相似的功能&#xff0c;但它们之间还是存在一些…

DownUnderCTF web sniffy

题目中给了源码 在index.php中将flag的值赋给了session[flag] session[theme]接收GET传入的theme参数。。。??是PHP中的空合并运算符它的作用是检查左侧的值是否存在且不为null。如果存在&#xff0c;则返回左侧的值&#xff1b;如果不存在&#xff0c;则返回右侧的值。 …

用友U8接口-采购管理(8)

概括 本文的操作需要正确部署U8API主要讲述采购管理接口的使用&#xff0c;以采购订单为例&#xff0c;其他单据接口都是大同小异的&#xff01;许多时候先在ERP做个单&#xff0c;然后仿造ERP单据参数&#xff0c;构造接口JSON参数是不错的做法哦 ERP单据金额计算 在ERP的许…

3DCAT亮相2024中国国际消费电子博览会,引领AI潮流

2024年10月18日-20日&#xff0c;备受瞩目的2024中国国际消费电子博览会&#xff08;以下简称“电博会”&#xff09;在青岛国际会展中心&#xff08;红岛馆&#xff09;盛大开幕。作为消费电子领域的盛会&#xff0c;本次电博会吸引了国内外300多家企业参展&#xff0c;展示了…

android openGL ES详解——缓冲区VBO/VAO/EBO/FBO/离屏渲染

目录 一、缓冲区对象概念 二、分类 三、顶点缓冲区对象VBO 1、概念 2、为什么使用VBO 3、如何使用VBO 生成缓冲区对象 绑定缓冲区对象 输入缓冲区数据 更新缓冲区中的数据 删除缓冲区 4、VBO应用 四、顶点数组对象VAO 1、概念 2、为什么使用VAO 3、如何使用VAO…

Django-中间件(切面编程AOP)

自定义中间件 官网&#xff1a;中间件 | Django 文档 | Django 中间件使用多就在主应用创建&#xff0c;仅限于子应用就在子引用中创建中间件文件.py 之后在settings.py文件中去配置中间件,运行的时候会自动调用中间件 def simple_middleware(get_response):def middleware…

数据结构和算法-动态规划(1)-认识动态规划

认识动态规划 什么是动态规划 Dynamic Programming is a method used in mathematics and computer science to solve complex problems by breaking them down into simpler subproblems. By solving each subproblem only once and storing the results, it avoids redundan…

centos-LAMP搭建与配置(论坛网站)

文章目录 LAMP简介搭建LAMP环境安装apache&#xff08;httpd&#xff09;安装mysql安装PHP安装php-mysql安装phpwind LAMP简介 LAMP是指一组通常一起使用来运行动态网站或者服务器的自由软件名称首字母缩写&#xff1a;Linux操作系统&#xff0c;网页服务器Apache&#xff0c;…

网络文件系统nfs实验1

服务端&#xff1a; 这个指令是搜索nfs相关的软件包 安装nfs相关的软件包&#xff1a; 列出已安装的nfs-utils软件包中的文件列表&#xff1a; 写配置文件&#xff1a;允许192.168.234.0/24这个网段的客户端能读写这个路径 重新导出所有当前已导出的文件系统&#xff1a; 启动…

DSPy:不需要手写prompt啦,You Only Code Once!

论文地址&#xff1a;https://arxiv.org/abs/2310.03714   项目地址&#xff1a;https://github.com/stanfordnlp/dspy 文章目录 1. 背景2. 签名3. 模块3.1 预测模块3.2 其他内置模块 4. 提词器5. 评估目标6. 代码分析6.1 _prepare_student_and_teacher6.2 _prepare_predicto…

【Docker】- WARNING: Found orphan containers XXX for this project.

报错展示 Creating network "net-10.9.0.0" with the default driver WARNING: Found orphan containers (server-4-10.9.0.8, server-3-10.9.0.7, server-1-10.9.0.5, server-2-10.9.0.6) for this project. If you removed or renamed this service in your compos…

Tomcat隐藏版本号和报错信息

为了避免漏洞扫描的时候造成版本泄露&#xff0c;可以在conf/server.xml配置文件中的<Host>配置项中添加如下配置: <Valve className"org.apache.catalina.valves.ErrorReportValve" showReport"false" showServerInfo"false" /> …

springboot案例

查询全部部门 项目结构 1. controller层 //日志注解,可以直接使用日志对象log.info Slf4j //用于指定将方法返回的对象转换为 JSON 或 XML 格式的响应体 RestController//DeptController.java //日志注解,可以直接使用日志对象log.info Slf4j //用于指定将方法返回的对象转换为…

Face Swap API 的整合与使用手册

Face Swap API 的整合与使用手册 Face Swap API 是一款功能强大的工具&#xff0c;能够通过提供一张源图像和一张目标图像&#xff0c;将目标图像中的人脸巧妙地替换为源图像中对应的位置。 在本手册中&#xff0c;我们将逐步指导您如何整合 Face Swap API&#xff0c;以便您…

Python金色流星雨

系列目录 序号直达链接爱心系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4Python李峋同款可写字版跳动的爱心5Python流星雨代码6Python漂浮爱心代码7Python爱心光波代码8Python普通的玫瑰花代码9Python炫酷的玫瑰花代码10Python多…

Linux:编辑器Vim和Makefile

✨✨所属专栏&#xff1a;Linux✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ vim的三种常用模式 分别是命令模式&#xff08;command mode&#xff09;、插入模式&#xff08;Insert mode&#xff09;和底行模式&#xff08;last line mode&#xff09; 各模式的功能区分如下&…

使用C#学习Office文件的处理(pptx docx xlsx)

Office文件 是指PPT 、word、Excel 这些常用工具生成的文件 &#xff0c;例如 pptx docx xlsx。 这些文件的读取和生成有很多很多库 例如 NOPI 、DevExpress、C1、Aspose、Teleric 等等&#xff0c;各有各的优缺点。俺今天不讲这个&#xff0c;俺只是讲讲如何了解Office文件的…