【leetcode】01背包总结

01 背包

关键点

  • 容器容量固定
  • 每件物品只有两种状态:不选、选 1 件
  • 求最大价值

代码

int N, W; // N件物品,容量为W
int w[N], v[N]; // w为大小,v为容量/* 数组定义 */
int[][] dp = new int[N][W + 1]; // 注意是W + 1, 因为重量会取到W
dp[i][j]; // 从下标为[0, i]的物品中选若干件物品(注意是若干件,不是全部),放入大小为j的容器时的最大价值/* 递推公式 */
// 由于每件物品有选、不选两种状态,所以两种状态取最大值即可
// 比如,对于dp[i][j]来说,
// 如果不选下标为i的物品,那么价值 = dp[i - 1][j];
// 如果选下标为i的物品,那么价值 = dp[i - 1][j - w[i]] + v[i]
// 两种状态取max
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);/* 初始化 */
// 从递推公式可以看出来,会存在dp[0][j]、dp[i][0]
// 对于dp[0][j]来说,回到数组定义,即选择下标为0的物品,放入大小为j的容器时的最大价值
// 这里又分两种情况,下标为0的物品的大小w[0]比j大,即放不进去,那么dp[0][j]=0;w[0]比j小,即放的进去,那么dp[0][j]=v[0]
// 对于dp[i][0]来说,回到数组定义,即从下标为[0, i]的物品中选若干件物品,放入大小为0的容器时的最大价值
// 显然dp[i][0] = 0
for(int i = 0; i < N; i ++ ) dp[i][0] = 0;
for(int j = 0; j <= W; j ++ ) {if (j >= w[0]) dp[0][j] = v[0];else dp[0][j] = 0;
}/* 求解 */
// 先遍历物品、再遍历大小
// 由于dp[0]j]已经初始化过了,所以从第1件物品开始循环
// 由于dp[i][0]已经初始化过了,所以从重量为1开始循环
for(int i = 1; i < N; i ++ )for(int j = 1; j <= W; j ++ ) {if (j < w[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i];}/* 输出答案 */
// dp[N - 1][W]: 即从[0, N-1]中选若干件物品,放入大小为W的容器时的最大价值
return dp[N - 1][W]; 

时间复杂度:O(NW)
空间复杂度:O(NW)

优化:滚动数组,二维空间 -> 一维空间

递推公式

滚动数组思想:
回到递推公式:
d p [ i ] [ j ] = M a t h . m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w [ i ] ] + v [ i ] ) dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]) dp[i][j]=Math.max(dp[i1][j],dp[i1][jw[i]]+v[i])

可以看出,第一维只用到了相邻两层的一个状态,没有第三个状态了。那实际上这个维度就可以去掉了,相当于用一个变量表示两个状态,赋值前就是上一层的状态,赋值后就是下一层的状态!
所以递推公式变成:
d p [ j ] = M a t h . m a x ( d p [ j ] , d p [ j − w [ i ] ] + v [ i ] ) dp[j]=Math.max(dp[j],dp[j-w[i]]+v[i]) dp[j]=Math.max(dp[j],dp[jw[i]]+v[i])

容量 W 遍历方向

对于遍历方向也需要改。如下图所示:

可以看出,后面的状态是用到了前面的状态的。如果还是像二维一样从左往右遍历的话,会出现这种情况:dp[j-w[i]] 被物品 i 更新了,dp[j] 也被物品 i 更新了。由于 dp[j] 是由 dp[j-w[i]] 更新过来的,相当于在重量为 j 的容器里放了两次物品 i,这和 01 背包的题目含义就冲突了:每件物品只有选和不选两种状态。
所以就要保证 dp[j] 用到的 dp[j-w[i]] 是没有被更新过的。方法也容易,反过来遍历就可以了。

初始化

一维的初始化实际上就是初始化一个物品都没用到时的价值。显然,对于任何重量的容器,不放物品,价值就是 0。初始化代码:

for(int j = 0; j <= W; j ++ ) dp[j] = 0;

容量 W 和物品 N 的遍历顺序

对于二维的形式,两种遍历方式都可以,因为不管怎么样,dp[i][j] 都是被左上角的状态更新的,所以先更新左上角的哪一个,实际上都一样。但是对于一维的形式,情况就不一样了。详细看一下两种方法的遍历的含义:

  1. 先遍历容量 W,再遍历物品 N:(×)
for(int j = W; j >=0; j -- )for(int i = 0; i < N; i ++ ) {if (j >= w[i]) dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);}

这种方法,相当于是对于同一个重量 j,从 N 个物品中选 1 个,看哪个价值最大。那最后 dp[W] 表示的就是从 N 个物品中选 1 个物品时的最大价值,这显然与 01 背包的题目含义冲突了:从 N 个物品中选若干个物品时(每个物品只选一次)的最大价值。
进一步分析,为什么这种方法每次都只会用到一个物品?因为每次递推用到的 dp[j-w[i]] 都是 0,因为 j 是从大到小遍历的,递推公式相当于变成了 dp[j]=Math.max(dp[j], v[i]),那不就是 N 个物品取最大值吗?所以也进一步加深了对一维递推公式的理解:要利用一维的递推,dp[j-w[i]] 一定要叠加了前面选了物品的状态。

  1. 先遍历物品 N,再遍历容量 W:(√)
for(int i = 0; i < N; i ++ )for(int j = W; j >=0; j -- ) {if (j >= w[i]) dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);}

这种方法,是先考虑前面物品的使用情况,放到 dp 中去,然后再开始考虑下一件物品。所以这种方法中,dp[j - w[i]] 是叠加了前面选了若干件物品之后的状态的。
因此,应该选择第二种遍历顺序。

时空复杂度

时间复杂度:O(NW)
空间复杂度:O(W),空间上比二维少了一个数量级

完整代码

int N, W; // N件物品,容量为W
int w[N], v[N]; // w为大小,v为容量/* 数组定义 */
int[] dp = new int[W + 1]; // 注意是W + 1, 因为重量会取到W
dp[j]; // 大小为j的容器时的最大价值/* 递推公式 */
dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);/* 初始化 */
for(int j = 0; j <= W; j ++ ) dp[j] = 0;/* 求解 */
// 每迭代一次i,dp就多叠加了一件物品的状态
for(int i = 0; i < N; i ++ )for(int j = W; j >= 0; j -- ) {if (j >= w[i]) dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);}/* 输出答案 */
// dp[W]: 大小为W的容器时的最大价值
return dp[W]; 

题目

https://kamacoder.com/problempage.php?pid=1046

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

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

相关文章

向日葵企业“云策略”升级 支持Android 被控策略设置

此前&#xff0c;贝锐向日葵推出了适配PC企业客户端的云策略功能&#xff0c;这一功能支持管理平台统一修改设备设置&#xff0c;上万设备实时下发实时生效&#xff0c;很好的解决了当远程控制方案部署后&#xff0c;想要灵活调整配置需要逐台手工操作的痛点&#xff0c;大幅提…

小型洗衣机哪个牌子好用又耐用?最好用的迷你洗衣机推荐

最近这两年在洗衣机中火出圈的内衣洗衣机&#xff0c;它不仅可以清洁我们较难清洗的衣物&#xff0c;自带除菌功能&#xff0c;可以让衣物上的细菌&#xff0c;还能在清洗的过程中呵护我们衣物的面料&#xff0c;虽然说它是内衣洗衣机&#xff0c;它的功能不止可以清洗内衣&…

精通Python第16篇—深入解析Pyecharts极坐标系参数与实战

文章目录 Pyecharts绘制多种炫酷极坐标系参数说明与方向的技术博客1. 导入必要的库2. 极坐标系基础3. 定制化极坐标系4. 方向性的极坐标系5. 极坐标系的动画效果6. 自定义极坐标轴标签7. 添加极坐标系的背景图8. 极坐标系的雷达图总结 Pyecharts绘制多种炫酷极坐标系参数说明与…

JVM系列——对象管理

JVM对象分布 对象头 第一类是用于存储对象自身的运行时数据&#xff0c;如哈希码&#xff08;HashCode&#xff09;、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等 另外一部分是类型指针&#xff0c;即对象指向它的类型元数据的指针&#xff0c;Java 虚…

DeepSORT算法实现车辆和行人跟踪计数和是否道路违规检测(代码+教程)

DeepSORT算法是一种用于目标跟踪的算法&#xff0c;它可以对车辆和行人进行跟踪计数&#xff0c;并且可以检测是否存在道路违规行为。该算法采用深度学习技术来提取特征&#xff0c;并使用卡尔曼滤波器来估计物体的速度和位置。 DeepSORT算法通过首先使用目标检测算法来识别出…

1 月 27日算法练习-贪心

文章目录 扫地机器人分糖果最小战斗力差距谈判纪念品分组 扫地机器人 思路&#xff1a; 最优机器人清理方法&#xff1a;机器人清理方法先扫左边&#xff0c;有时间再扫右边。最短时间&#xff1a;通过枚举&#xff0c;从 1 开始&#xff0c;清理面积会越大直到全部面积的清理…

【Sql Server】新手一分钟看懂在已有表基础上增加字段和说明

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

计算机网络——IP协议

前言 网络层的主要负责地址分配和路由选择,ip负责在网络中进行数据包的路由和传输。 IPv4报文组成&#xff08;了解&#xff09; IPv4首部&#xff1a;IPv4首部包含了用于路由和传输数据的控制信息&#xff0c;其长度为20个字节&#xff08;固定长度&#xff09;。 版本&#…

单片机14-17

目录 LCD1602 LCD1602液晶显示屏 直流电机驱动&#xff08;PWM&#xff09; LED呼吸灯 直流电机调速 AD/DA&#xff08;SPI通信&#xff09; AD模数转换 DA数模转换 红外遥控&#xff08;外部中断&#xff09; 红外遥控 红外遥控电机调速 LCD1602 LCD1602液晶显示屏 …

Web 开发 6:Redis 缓存(Flask项目使用Redis并同时部署到Docker详细流程 附项目源码)

大家好&#xff01;欢迎来到第六篇 Web 开发教程&#xff0c;今天我们将探讨一个非常重要的话题&#xff1a;Redis 缓存。作为一个互联网开发者&#xff0c;你一定知道在处理大量请求时&#xff0c;性能优化是至关重要的。而 Redis 缓存正是帮助我们提升系统性能的利器。Redis …

四川古力未来科技公司抖音小店选品攻略从零到一

随着抖音的日益火爆&#xff0c;抖音小店也应运而生&#xff0c;成为了电商行业的新宠儿。但对于许多新手商家来说&#xff0c;如何从众多的商品中挑选出适合自己店铺的商品&#xff0c;却是一件非常头疼的事情。本文将为你揭秘抖音小店的选品攻略&#xff0c;让你轻松玩转电商…

A股风格因子看板 (2024.01 第10期)

该因子看板跟踪A股风格因子&#xff0c;该因子主要解释沪深两市的市场收益、刻画市场风格趋势的系列风格因子&#xff0c;用以分析市场风格切换、组合风格暴 露等。 今日为该因子跟踪第10期&#xff0c;指数组合数据截止日2023-12-31&#xff0c;要点如下 近1年A股风格因子检验…

网络安全面试你应该准备什么?

年底&#xff0c;刚过了校招和跳槽的密集阶段&#xff0c;有的同学拿到了心仪的Offer&#xff0c;有的同学却铩羽而归。星球里也有小伙伴提出了这样的问题&#xff1a;安全相关的工作&#xff0c;有没有什么面试技巧呢&#xff1f; 由于安全门类巨大&#xff0c;涉及的技术很多…

Python进阶(1) | 使用VScode写单元测试

Python进阶(1) | 单元测试 2024.01.28 VSCode: 1.85.1 Linux(ubuntu 22.04) 文章目录 Python进阶(1) | 单元测试1. 目的2. Python Profile3. 单元测试框架3.1 什么是单元测试3.2 选一个单元测试框架3.3 编写 Python 单元测试代码3.4 在 VSCode 里发现单元测试3.5 再写一个单元…

【MySQL】补充和navicat的一些简单使用

文章目录 前言在这里插入图片描述 事情起因因为这个articlecount的c是小写了&#xff0c;我想改成大写 一、修改二、navicat的使用步骤1.连接2.建库&#xff0c;建表 三.填写数据总结 前言 事情起因因为这个articlecount的c是小写了&#xff0c;我想改成大写 提示&#xff1a;…

Redis 学习笔记 2:Java 客户端

Redis 学习笔记 2&#xff1a;Java 客户端 常见的 Redis Java 客户端有三种&#xff1a; Jedis&#xff0c;优点是API 风格与 Redis 命令命名保持一致&#xff0c;容易上手&#xff0c;缺点是连接实例是线程不安全的&#xff0c;多线程场景需要用线程池来管理连接。Redisson&…

一文搞懂设计模式—策略模式

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 文章目录 使用场景策略模式实现策略模式的优缺点策略模式优化使用Map取消 Context 类策略枚举解决策略类膨胀SpringBoot中的策略模式 总结 在软件开发中&#xff0c;经常会…

pcl应用八叉树实例

pcl应用八叉树实例 文章目录 pcl应用八叉树实例1、基本概念2、基于八叉树的空间划分及搜索操作2.1、关键函数说明2.1.2 OctreePointCloudSearch 类2.1.2 voxelSearch 函数 3、无序点云数据集的空间变化检测 1、基本概念 八叉树结构通过循环递归的划分方法对大小为2 n ∗ 2 n ∗…

C++面试宝典第25题:阶乘末尾零的个数

题目 给定一个整数n,返回n!(n的阶乘)结果尾数中零的个数。 示例 1: 输入:3 输出:0 解释:3! = 6,尾数中没有零。 示例 2: 输入:5 输出:1 解释:5! = 120,尾数中有1个零。 解析 这道题主要考察应聘者对于数学问题的分析和理解能力,以及在多个解决方案中,寻求最优…

elementUI的el-select传递item对象或其他参数的2种方法

方法1 :value“item” 绑定对象 只要:value绑定item对象就可以 value-key"value" 必须是item里的一个属性&#xff0c;绑定值为对象类型时必填 <el-select v-model"value" placeholder"请选择" value-key"value" change"cha…