代码随想录算法训练营第四十二天 | 动态规划 part 4 | 01背包问题(二维、一维滚动数组)、416. 分割等和子集

news2024/11/24 5:09:30

目录

  • 01背包问题 二维
    • 代码
  • 01背包问题(一维滚动数组)
    • 代码
  • 416. 分割等和子集
    • 思路
    • 代码

01背包问题 二维

背包问题汇总:

在这里插入图片描述

二维数组dp——01背包五部曲

  1. dp[i][j]表示从下标为[0-i]的物品里面任意取,放进容量为j的背包,价值的总和最大是多少。
    在这里插入图片描述

  2. 递推公式:可以由两个方向推出dp[i][j]

    • 不放物品i:由dp[i - 1][j]推出
    • 放物品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后的最大价值。

    综上,对于来自两个方向的结果,我们取最大值即为dp[i][j]的最大值。所以,递推转换方程为,dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

  3. 初始化:根据递推公式,求dp[i][j]时我们需要确保它的左上角已经有值了。
    所以初始化代码如下:

    for (int j = 0 ; j < weight[0]; j++) {  // 当然这一步,如果把dp数组预先初始化为0了,这一步就可以省略,但很多同学应该没有想清楚这一点。
        dp[0][j] = 0;
    }
    // 正序遍历
    for (int j = weight[0]; j <= bagweight; j++) {
        dp[0][j] = value[0];
    }
    

    所以初始化的情况如下图:

    在这里插入图片描述
    其他的空格初始化的值可以是任意。

  4. 遍历顺序:
    关于两个for loop先loop哪个,先遍历背包还是先遍历物品。其实都可以,因为他们都满足dp[i][j]的递推公式。如下图:
    先遍历物品,再遍历背包:
    在这里插入图片描述
    先遍历背包,再遍历物品:

    实际上,如果想要倒序遍历也是可行的(详细参考一维滚动数组)。

代码

def backpack01_2d(weight, value, capacity):
    n = len(weight)
    dp = [[0 for _ in range(capacity+1)] for _ in range(n)]
    # initialize the dp table
    for j in range(weight[0], capacity+1):
        dp[0][j] = value[0]
    
    for i in range(1, n):
        for j in range(1, capacity + 1):
            if j >= weight[i]:
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
            else:
                dp[i][j] = dp[i - 1][j]
    print(dp)
    return dp[-1][-1]

# generate some example to run the defined function
weight = [1, 3, 4]
value = [15, 20, 30]
capacity = 4
print(backpack01_2d(weight, value, capacity))

01背包问题(一维滚动数组)

一维数组和二维数组的区别在于:一维的滚动数组其实是压缩的二维数组。

五部曲:

  1. dp[j]为容量为j的背包,所背的物品价值可以最大为dp[j]
  2. 递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
  3. 初始化:dp[0] = 0,都初始为零
  4. 遍历顺序:先遍历物品,再遍历背包,遍历背包需要倒序遍历,这样才能保证物品i只被放入一次。
    能够将遍历物品和遍历背包的顺序颠倒吗?
    因为一维dp的写法,背包容量一定是要倒序遍历(原因上面已经讲了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。

代码

def test_1_wei_bag_problem(weight, value, bagWeight):
    # 初始化
    dp = [0] * (bagWeight + 1)
    for i in range(len(weight)):  # 遍历物品
        for j in range(bagWeight, weight[i] - 1, -1):  # 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

    return dp[bagWeight]

weight = [1, 3, 4]
value = [15, 20, 30]
bagweight = 4
print(test_1_wei_bag_problem(weight, value, bagweight))

416. 分割等和子集

Leetcode

在这里插入图片描述

思路

二维数组:

  1. dp[i][j]为在0-i之间的数字选择,能否相加之和为j
  2. 递推公式:dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]] 可以选择或者不选择nums[i]这个数字
  3. 初始化都为False, 然后dp[0][nums[0]] = True
  4. 遍历顺序和01背包一样

一位滚动数组:
对于滚动数组,我们将其理解成01背包的形式。每个数字的重量和价值都等于数字。比如说5的重量和价值都是5。

  1. dp[j]表示背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]
  2. dp[j] = max(dp[j], dp[j - num] + num),相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]

代码

二维数组

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        _sum = sum(nums)
        if _sum % 2 != 0:
            return False
        
        total = _sum // 2
        # 初始化 * 10001 是根据题目而定的最大可能距离
        # 为了避免像 [100] 这种edge case
        dp = [[False] * (10001) for _ in range(len(nums))]


        dp[0][nums[0]] = True

        for i in range(1, len(nums)):
            for j in range(1, total + 1):
                if j >= nums[i]:
                    dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
                else:
                    dp[i][j] = dp[i - 1][j]

        return dp[-1][total]

  • 时间复杂度: O(n^2)
  • 空间复杂度: O(n^2)

一维滚动数组

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        _sum = sum(nums)
        if _sum % 2 != 0:
            return False
        
        total = _sum // 2
        dp = [0] * (total + 1)
        for num in nums:
            for j in range(total, num - 1, -1):
                dp[j] = max(dp[j], dp[j - num] + num)

        return dp[-1] == total
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(n)

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

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

相关文章

Redis入门 (店铺营业状态设置) --苍穹外卖day4

目录 redis简介 redis下载与安装 redis服务启动与停止​编辑 redis数据类型 五种常用数据类型 各个类型特点 redis常用命令 字符串 哈希 列表 集合 有序集合 通用指令 ​在Java中操作Redis 导入坐标 编写配置类​ 通过RedisTem~对象操作 字符串 ​哈希 列…

什么是CDN

CDN是什么&#xff1f; 假设有一个IP为100.1.1.1的服务器网站&#xff0c;在没有CDN的情况下&#xff0c;PC先访问该服务器的DNS服务器&#xff0c;经过域名解析后再去访问服务器&#xff0c;如下图&#xff1a; 如果部署了CDN&#xff0c;这时候就有一个能识别CDN节点的DNS服…

经过打包后运行app.exe文件之后问题解决

项目文件经过python代码如何打包方法打包之后文件目录是下面这样的。 按照下面的路径运行app.exe文件报错&#xff1a; RuntimeError: Unable to open E:\face_detection\dist\app\face_recognition_models\models\shape_predictor_68_face_landmarks.dat [35816] Failed to…

AI在线工具分享

1、ChatGPT ChatGPT是一种由OpenAI训练的大型语言模型。它的原理是基于Transformer架构&#xff0c;通过预训练大量文本数据来学习如何生成人类可读的文本&#xff0c;然后通过接受输入并生成输出来实现对话。 ChatGPT的用途非常广泛&#xff0c;可以用于自然语言处理&#xf…

Seata入门系列【1】安装seata 1.7.1+nacos 2.1.1

1 介绍 Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式解决方案。 Github: https://github.com/seata/seata 官方文档&#xff1a;h…

短期光伏发电量短期预测(Python代码,先对异常值处理,再基于XGBoost模型预测)

一.代码流程&#xff08;运行效果&#xff1a;短期光伏发电量短期预测&#xff08;Python代码&#xff0c;先对异常值处理&#xff0c;再基于XGBoost模型预测&#xff09;_哔哩哔哩_bilibili 模型流程&#xff1a; 导入所需的库&#xff0c;包括NumPy、Pandas、Matplotlib、Sea…

TS编译选项——TS文件编译后消除注释

在tsconfig.json文件中配置removeComments属性 {"compilerOptions": {// outDir 用于指定编译后文件所在目录"outDir": "./dist", // 将编译后文件放在dis目录下// 是否文件编译后移除注释"removeComments": true} } 左边是编写的t…

通过解读yolov5_gpu_optimization学习如何使用onnx_surgon

onnx实战一: 解析yolov5 gpu的onnx优化案例: 这是一个英伟达的仓库, 这个仓库的做法就是通过用gs对onnx进行修改减少算子然后最后使用TensorRT插件实现算子&#xff0c; 左边是优化过的, 右边是原版的。 通过这个案例理解原版的onnx的导出流程然后我们看英伟达是怎么拿gs来优化…

伦敦银如何选择最优的交易方法

经常有投资者会问&#xff0c;伦敦银投资中如何选择最好的方法呢&#xff1f;我们进行伦敦银投资&#xff0c;目的就是找到一个能够盈利的交易方法&#xff0c;它能够使我们大部分交易都是盈利&#xff0c;少部分交易亏损&#xff0c;但是可以将亏损控制在一定的范围之内&#…

Windows10关闭此电脑“桌面”“图片”“视频”“3D对象”“文档”等显示,只显示“设备与驱动器”

如何关闭下图"文件夹"等7个子文件夹&#xff0c;只显示“设备和驱动器”&#xff1f; 关闭步骤&#xff1a; 打开cmd,输入regedit打开注册表编辑器打开注册表编辑器后&#xff0c;定位到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\My…

会声会影和剪映哪个好,2023年全新功能对比详细解析

随着网络视频的蓬勃发展&#xff0c;越来越多的人开始涉足视频剪辑领域&#xff0c;毕竟技多不压身嘛。在众多剪辑软件中&#xff0c;剪映和会声会影是备受新手青睐的两种。那么&#xff0c;会声会影和剪映哪个好呢&#xff1f;在它们之间&#xff0c;哪一个更适合初学者呢接&a…

软件测试之Web安全测试详解

前言 随着互联网时代的蓬勃发展&#xff0c;基于Web环境下的应用系统、应用软件也得到了越来越广泛的使用。 目前&#xff0c;很多企业的业务发展都依赖于互联网&#xff0c;比如&#xff0c;网上银行、网络购物、网络游戏等。但&#xff0c;由于很多恶意攻击者想通过截获他人…

ASCII码-对照表

ASCII 1> ASCII 控制字符2> ASCII 显示字符3> 常用ASCII码3.1> 【CR】\r 回车符3.2> 【LF】\n 换行符3.3> 不同操作系统&#xff0c;文件中换行 1> ASCII 控制字符 2> ASCII 显示字符 3> 常用ASCII码 3.1> 【CR】‘\r’ 回车符 CR Carriage Re…

如何通过git指令加入管理者仓库并提交分支(Github Gitee)

文章目录 创建GitHub、Gitee账户安装git下载gitgit基础配置 管理者创建gitee仓库新建仓库配置公钥 管理者管理仓库开发者通过git指令提交git提交错误原因&#xff1a; 创建GitHub、Gitee账户 GitHub&#xff1a; https://github.com/ Gitee &#xff1a; https://gitee.com/ …

redis做缓存(cache)

什么是缓存 缓存(Cache)的核心思路就是把一些常用的数据放到访问速度更快的地方&#xff0c;方便获取。关于硬件的访问速度来说 CPU寄存器>内存>硬盘>网络 因此常见使用内存作为硬盘的缓存&#xff0c;例如redis。使用硬盘作为网络的缓存&#xff0c;例如浏览器通过h…

2023-9-25 货仓选址

题目链接&#xff1a;货仓选址 #include <iostream> #include <algorithm>using namespace std;const int N 100010;int n; int a[N];int main() {cin >> n;for(int i 0; i < n; i ) cin >> a[i];sort(a, a n);int res 0;for(int i 0; i < …

生产管理看板系统助力高压线束生产车间实现生产任务的可视化管理

随着企业对生产智能化的追求不断提升&#xff0c;生产现场设备联网进行数据采集成为实现生产智能化的第一步&#xff0c;也是打造并实现数字化工厂最基础的一步。在这个过程中&#xff0c;生产管理看板系统发挥着重要的作用&#xff0c;能够实时在线统计车间工业生产设备的运行…

删除链表的倒数第N个节点-双指针法

【题目描述】 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 【示例】 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 输入&#xff1a;head [1], n 1 输出&#xff1a;[] 输入&#xff1a;head [1,2], n …

CHAPTER 11: DESIGN A NEWS FEED SYSTEM

Step 1 - Understand the problem and establish design scope Candidate: Is this a mobile app? Or a web app? Or both? Interviewer: Both Candidate: What are the important features? Interview: A user can publish a post and see her friends’ posts on the ne…

《红警3》因计算机中丢失d3dx9_35.dll无法打开游戏怎么办?最新解决方法推荐

d3dx9_35.dll 是 DirectX 9.0c 的一部分&#xff0c;它是一个动态链接库 (DLL)&#xff0c;包含了许多用于支持 DirectX 9.0c 功能的函数和类。DirectX 是一种由微软开发的游戏和多媒体应用程序编程接口&#xff0c;它提供了许多功能&#xff0c;如 3D 图形、音频、输入等&…