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

news2024/11/16 20:39:39

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 = 4
    print(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 的子集
  • 背包中的每一个元素是不可重复放入的

python代码解法(使用二维数组)

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        
        if sum(nums) % 2 == 1:
            return False
        target = sum(nums) // 2
        dp = [[False for _ in range(target + 1)] for _ in range(len(nums))]

        if nums[0] <= target:
            dp[0][nums[0]] = True
        for i in range(1, len(nums)):  # 遍历物品
            for j in range(0, target + 1):  # 遍历背包容量
                dp[i][j] = dp[i - 1][j]

                if nums[i] == j:
                    dp[i][j] = True
                    continue
                if nums[i] < j:
                	# 1.不选择 nums[i]:说明在[0, i - 1]这个子区间内已经有一部分元素使得它们的和为 j
                	# 2.选择 nums[i],如果在[0, i - 1]这个子区间内找到一部分元素使得它们的和为 j - nums[i]
                    dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]

        return dp[len(nums) - 1][target]

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 False
        nums.sort()
        target = sum(nums) // 2
        # 题目说每个数组中的元素不会超过100,数组的大小不会超过200
        # 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了
        dp = [0] * 10001
        for 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 True
        return 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] = 0
        for 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.coloradmin.cn/o/1526508.html

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

相关文章

HTML学习:图片格式——超链接

一、图片格式 1.jpg格式 概述:扩展名为.jpg 或.jpeg &#xff0c;是一种有损的压缩格式(把肉眼不容易观察出来的细节丢弃了)。 主要特点:支持的颜色丰富、占用空间较小、不支持透明背景、不支持动态图。 使用场景:对图片细节没有极高要求的场景&#xff0c;例如:网站的产品…

容器数据卷

目录 一、容器数据卷概念 二、使用数据卷 2.1直接使用命令来挂载 三、实战测试 四、具名挂载和匿名挂载 4.1匿名挂载举例&#xff1a; 4.2具名挂载举例&#xff1a; 五、数据卷容器 一、容器数据卷概念 数据&#xff1f;如果数据都在容器中&#xff0c;那么容器删除&am…

linux(Ubuntu22) 一篇带你学会Linux,详细篇

Linux 简介 精通Linux&#xff0c;自带python&#xff0c;系统开源 电脑可安装双系统 c盘安装win D盘安装linux 在一套硬件上只能同时运行一个操作系统 虚拟机 模拟真实环境 在虚拟机内运行操作系统 需要硬件支持虚拟化 开启VT-X VM…

Web基础06-AJAX,Axios,JSON数据

目录 一、AJAX 1.概述 2.主要作用 3.快速入门 4.AJAX的优缺点 &#xff08;1&#xff09;优点 &#xff08;2&#xff09;缺点 5.同源策略 二、Axios 1.概述 2.快速入门 3.请求方式别名 三、JSON 1.概述 2.主要作用 3.基础语法 4.JSON数据转换 &#xff08;1…

洛谷_P1152 欢乐的跳_python写法

思路&#xff1a; 这道题我用到了集合的互异性来判断这组数字是否满足条件我觉得是比较有效一点的。 data list(map(int,input().split())) data data[1:] l [i for i in range(1,len(data))] s [] for i in range(len(data)-1):s.append(abs(data[i] - data[i1]))if set(…

RVA和FOA转换---三

文章目录 修改初始值RVA和FOA转换RVAFOARVA和FOA的关系 本次内容包含如何修改程序中的初始值&#xff0c;和如何转换内存和文件的地址。 修改初始值 问题&#xff1a; 我们写了一个程序&#xff0c;可以输出一个结果&#xff0c;那么我们可以通过修改PE文件来改变这个输出结果…

平时到点就走的00后,居然一跃上岸字节,真的难以接受....

又到了一年一度的求职旺季金&#xff01;三&#xff01;银&#xff01;四&#xff01;在找工作的时候都必须要经历面试这个环节。在这里我想分享一下自己上岸字节的面试经验&#xff0c;过程还挺曲折的&#xff0c;但是还好成功上岸了。大家可以参考一下&#xff01; 0821测评 …

SpringBoot异常:类文件具有错误的版本 61.0, 应为 52.0的解决办法

问题&#xff1a; java: 无法访问org.mybatis.spring.annotation.MapperScan 错误的类文件: /D:/Program Files/apache-maven-3.6.0/repository/org/mybatis/mybatis-spring/3.0.3/mybatis-spring-3.0.3.jar!/org/mybatis/spring/annotation/MapperScan.class 类文件具有错误的…

PSCA系统控制集成之复位层次结构

PPU 提供以下对复位控制的支持。 • 复位信号Reset signals&#xff1a;PPU 提供冷复位和热复位输出信号。PPU 还为实现部分保留的电源域管理提供了额外的热复位输出信号。 • 电源模式控制Power mode control&#xff1a;PPU 硬件适当地管理每个支持的电源模式转换的复位信号…

Python库Gym:打开机器学习与强化学习的大门

Python库Gym&#xff1a;打开机器学习与强化学习的大门 强化学习作为人工智能领域的重要分支&#xff0c;已经在各种领域展现出了巨大的潜力。为了帮助开发者更好地理解和应用强化学习算法&#xff0c;Python库Gym应运而生。Gym提供了一个开放且易于使用的环境&#xff0c;供开…

深入解析:在 Node.js 中删除文件的正确姿势

引言 在 Node.js 中处理文件尤其是移除文件&#xff0c;对于维护高效应用程序至关重要。储存和秩序当道的今天&#xff0c;删除不必要或冗余的文件能力显得尤为关键。本文深入探讨你会想要使用这个强大功能的时刻和原因&#xff0c;并通过各种案例展示了这个概念&#xff0c;同…

一、从0开始卷出一个新项目之CC-Link IE TSN 概述

目录 1.1 范围 1.2 目的 1.3 CC-Link介绍 1.3.1 CPLA协会 1.3.2 CC-Link协议家族 1.3.3 CC-Link IE TSN 二、瑞萨RIN32M4-CL3 2.1 芯片简介 2.2 资料下载 2.3 RIN系列产品 一、从0开始卷出一个新项目之CC-Link IE TSN 概述 一、CC-Link介绍 1.1 范围 快速实现CC-Lin…

TrueNAS怎么设置中文,最新2024版本安装详细说明

首先我们做好安装前的准备工作 1&#xff0c;ISO镜像安装包 2&#xff0c;虚拟机&#xff08;建议使用ESXI虚拟机环境&#xff09; 如果是物理机安装&#xff0c;建议先给底层安装虚拟机系统esxi&#xff0c;再在上面安装方便以后的管理&#xff0c;如果你想物理机直接安装&a…

心灵治愈交流平台|基于springboot框架+ Mysql+Java+B/S结构的心灵治愈交流平台设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 用户功能模块 心理咨询师功能 系统功能设计 数据库…

【C++】三大特性之多态

1 定义及实现 1.1 概念 多态是C三大特性之一。通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 多态是在不同继承关系的类对象&#xff0c;去调用同一函数&#xff0c;产生了不同的行为。比如学…

基于vue实现bilibili网页

学校要求的实验设计,基于vue实现bilibili网页版,可实现以下功能 (1)基本的悬浮动画和页面渲染 (2)可实现登录和未登录的页面变化 (3)在登录页面的,实现密码判断,或者短信验证方式的倒数功能 (4)实现轮播图 (5)实现预览视频(GIF) (6)页面下拉到一定高度出现top栏以及右下角的返回…

CentOS 7 编译安装 Nginx

CentOS 7 编译安装 Nginx 背景下载 Nginx 源码包安装依赖包编译添加环境变量添加守护查考文献 背景 一开始使用 docker 搭建了一个 web 服务器&#xff0c;但是由于 docker 不太方便的部署 TLS 证书&#xff0c;故使用 Nginx 做反向代理&#xff0c;实现 https 连接。 下载 N…

观成科技-基于自适应学习的人工智能加密流量检测技术

1.前言 人工智能技术的广泛应用正在深刻改变我们的生活。在网络安全领域&#xff0c;基于机器学习的检测技术也应用在许多场景中。随着信息技术的迅猛发展和数字化转型的深入推进&#xff0c;加密技术逐渐成为保障网络安全和数据隐私的核心手段&#xff0c;而基于机器学习的检…

LeetCode 面试经典150题 274.H指数

题目&#xff1a; 给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。 根据维基百科上 h 指数的定义&#xff1a;h 代表“高引用次数” &#xff0c;一名科研人员的 h 指数 是指他&#xff08;她…