算法练习-01背包问题【含递推公式推导】(思路+流程图+代码)

news/发布时间2024/5/14 6:43:07

难度参考

        难度:困难

        分类:动态规划

        难度与分类由我所参与的培训课程提供,但需 要注意的是,难度与分类仅供参考。且所在课程未提供测试平台,故实现代码主要为自行测试的那种,以下内容均为个人笔记,旨在督促自己认真学习。

题目

        动态规划经典问题01背包?

        具体内容:

        背包最大重量为4

        物品如下:

重量价值
物品0115
物品1320
物品2430

        问背包能背的最大重量是多少?

思路

        0-1 背包问题的动态规划解法基于以下思路:

  1. 子问题定义

    • 将原问题分解为子问题。在这里,我们考虑"对于给定的背包容量和一组物品,最大价值是多少?"
    • 定义状态 dp[i][w] —— 表示考虑前 i 个物品时,对于不超过 w 重量的背包,所能得到的最大价值。
  2. 初始条件

    • 对于 dp[0][...],即一个物品也不考虑的情况,背包的价值总是 0。
    • 对于 dp[...][0],即背包容量为 0 的情况,背包的价值也总是 0。
  3. 递推关系(状态转移方程):

    • 对于每一个物品 i 和每一个可能的重量 w,我们需要做出一个决策:是否将物品 i 放入背包。
    • 如果不放物品 i,则 dp[i][w] = dp[i-1][w] —— 也就是说,当前的最大价值与前 i-1 个物品时的最大价值相同。
    • 如果放物品 i,我们必须确保当前背包的剩余容量至少是物品 i 的重量 (weights[i-1] <= w),在这种情况下,dp[i][w] = dp[i-1][w-weights[i-1]] + values[i-1] —— 也就是说,当前的最大价值是物品 i 的价值加上剩余重量在前 i-1 个物品中能得到的最大价值。
  4. 填表

    • 我们按照递推关系来填表 —— 从较小的 i 和 w 开始,直到 i 等于物品个数,w 等于背包最大重量。
  5. 解的构造

    • 表格填完后,dp[n][maxWeight] 就是答案 —— 即考虑所有物品和最大背包重量时的最大价值。

        补充-递推公式的推导:

        假设我们有n个物品,每个物品的重量为weights[i],价值为values[i],背包的最大重量限制为maxWeight。我们定义dp[i][w]为考虑前i个物品(物品编号为0i-1),在背包重量限制为w的情况下,能够得到的最大总价值。我们想求的最终答案是dp[n][maxWeight]

        对于每个物品i(从1开始计数,对应数组下标为i-1),以及每个可能的重量限制w,我们面临两种选择:

  1. 不包含当前物品:如果我们决定不包含当前考虑的物品i-1,那么最大价值不会因为当前物品的存在而改变,所以它等于没有考虑当前物品时的最大价值,即dp[i-1][w]

  2. 包含当前物品:如果我们决定包含当前考虑的物品i-1,前提是这个物品的重量不超过当前的重量限制w(即weights[i-1] <= w)。此时,我们需要加上这个物品的价值values[i-1],同时,背包的剩余重量变为w - weights[i-1],因此我们还需要加上在新的重量限制下,考虑前i-1个物品时的最大价值,即dp[i-1][w-weights[i-1]] + values[i-1]

        因此,递推公式如下:

  • 如果weights[i-1] > w(当前物品重量超过背包限制),则dp[i][w] = dp[i-1][w]
  • 否则,dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1])

        这个递推公式基于以下逻辑:对于每个物品,我们都做出是否包含它的决定,以确保在不超过背包重量限制的情况下达到最大价值。这种方式确保了我们能够考虑所有可能的组合,并找到最优解。

示例

        初始状态

        首先,初始化一个表`dp`,其中`dp[i][j]`代表在只考虑前`i`个物品,且背包容量为`j`时的最大价值。初始化时,所有值均为0。

        物品情况:
        - 物品0:价值15,重量1
        - 物品1:价值20,重量3
        - 物品2:价值30,重量4

        动态规划表格是这样的(行表示物品,列表示重量):

重量        0   1   2   3   4
不选物品    0   0   0   0   0

        加入物品0

        加入物品0(价值15,重量1)后,我们更新表格。对于重量1及以上的列,我们可以加入物品0。

重量        0   1   2   3   4
不选物品    0   0   0   0   0
加入物品0   0  15  15  15  15

        加入物品1

        接着,我们考虑加入物品1(价值20,重量3)。我们更新表格,考虑对于每个重量限制,在不超过该重量的情况下,是否加入物品1能获得更高的价值。

重量        0   1   2   3   4
不选物品    0   0   0   0   0
加入物品0   0  15  15  15  15
加入物品1   0  15  15  20  20

        在重量为4时,我们比较加入物品1后的价值与之前的状态。但是,我们忽略了一种可能:加入物品1(重量3,价值20)后,还可以再加入物品0(重量1,价值15),因此,对于重量4,最大价值应该是35(物品0和物品1的组合)。

        加入物品2

        最后,我们考虑加入物品2(价值30,重量4)。同样,我们更新表格,考虑对每个重量限制,加入物品2是否能带来更高的价值。

重量        0   1   2   3   4
不选物品    0   0   0   0   0
加入物品0   0  15  15  15  15
加入物品1   0  15  15  20  35
加入物品2   0  15  15  20  35

        在重量为4的情况下,加入物品2的价值(30)并不比已有的组合(物品0和物品1,总价值35)更优,因此我们保持原有的最大价值不变。

        最终结果

        最终,我们得到的动态规划表如下,表明在背包重量限制为4时,我们能获得的最大价值是35,通过组合物品0(价值15,重量1)和物品1(价值20,重量3)。

重量        0   1   2   3   4
不选物品    0   0   0   0   0
加入物品0   0  15  15  15  15
加入物品1   0  15  15  20  35
加入物品2   0  15  15  20  35

        因此,最终答案是,在确保总重量不超过4的条件下,我们最后得到的背包中的最大价值是35。

梳理

        这个方法能够实现的原因在于它利用了动态规划(Dynamic Programming, DP)的两个关键性质:最优子结构和重叠子问题。

        最优子结构意味着问题的最优解包含着其子问题的最优解。对于0-1背包问题来说,每增加一个物品的选择,都是基于之前所有物品选择的最优解上进行的新增决定。换句话说,在考虑是否加入当前物品时,我们可以依赖于之前物品的决策结果,这些决策结果是以最大化价值为目标的。

        重叠子问题指的是在解决问题的过程中,相同的问题会被多次解决。在0-1背包问题中,当我们分别计算每一个重量限制下的最大价值时,会重复计算某些重量限制下的最大价值。通过动态规划,我们可以将这些价值存储在一个表中,避免重复计算,这就是为什么我们使用一个二维数组来存储每个子问题的答案。

        在填充动态规划表时,我们从最小的子问题开始,即背包没有任何物品时的价值是0。然后逐步增加物品和重量,直到考虑了所有物品和所有重量限制。在每一步,针对每一个重量限制,我们都计算了两种情况:

        1. 不加入当前的物品,直接使用之前同样重量限制下的最大价值。
        2. 尝试加入当前的物品,将物品的价值加上剩余重量限制下的最大价值。

        通过比较上述两种情况的价值,我们可以选择较大的一个作为当前重量限制和当前物品下的最大价值。这个过程一直持续到表格被填满,最终我们在表格的右下角得到的值就是我们能够拿到的最大价值。

        简单来说,这方法之所以行之有效,是因为它将一个复杂问题分解为一系列叠加的简单问题并解决它们,同时保留了要求的全局最优解。通过表格的逐步填充,我们保证了在任何给定重量限制下,我们都是在做出最佳决策,以此计算出全局最优解。

代码

#include <iostream> // 引入标准输入输出库
#include <vector> // 引入向量(动态数组)库
using namespace std; // 使用标准命名空间,简化代码// 动态规划解决 0-1 背包问题的函数
int knapsack(const vector<int>& weights, const vector<int>& values, int maxWeight) {int n = weights.size(); // 物品数量// 初始化动态规划表,所有值起始为0vector<vector<int>> dp(n + 1, vector<int>(maxWeight + 1, 0)); // 使用动态规划构建 dp 数组for (int i = 1; i <= n; ++i) { // 遍历每个物品for (int w = 1; w <= maxWeight; ++w) { // 遍历每种重量限制// 如果当前物品可以加入背包if (weights[i - 1] <= w) {// 选择加入当前物品和不加入当前物品中价值更大的一个dp[i][w] = max(dp[i - 1][w], values[i - 1] + dp[i - 1][w - weights[i - 1]]);} else {// 如果不能加入当前物品,保持之前的最大价值dp[i][w] = dp[i - 1][w];}}}// 返回最大值,即考虑所有物品且不超过最大重量限制时的最大价值return dp[n][maxWeight];
}int main() {int maxWeight = 4; // 背包的最大重量限制vector<int> weights = {1, 3, 4}; // 物品的重量vector<int> values = {15, 20, 30}; // 物品的价值// 输出背包能达到的最大总价值cout << "背包能达到的最大总价值为 "<< knapsack(weights, values, maxWeight) << endl;return 0; // 程序正常结束
}

        时间复杂度:O(n * maxWeight)

        空间复杂度:O(n * maxWeight)

打卡

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

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

相关文章

HTML 超文本标记语言

超文本标记语言 HTML 在一个客户程序主窗口上显示出的万维网文档称为页面 (page)。 页面制作的标准语言&#xff1a;HTML。 超文本标记语言 HTML (HyperText Markup Language) 是一种制作万维网页面的标准语言&#xff0c;它消除了不同计算机之间信息交流的障碍&#xff0c…

Python 文本处理和语义分析2 使用m3e对文本向量化

说明 向量化将会是下一阶段演进的目标。 在过去的实践中&#xff0c;向量或者矩阵其实是最贴近工具端的。 以sklearn为例&#xff0c;虽然原始数据可能还是自然语言&#xff0c;但是在最终执行 fit或者predict之前&#xff0c;数据一般都转为了矩阵形态(numpy)。也就是说&…

Java学习心得感悟

在我踏入Java学习的道路之前&#xff0c;我对编程只是一知半解&#xff0c;对于代码的世界充满了好奇和向往。然而&#xff0c;当我真正开始学习Java时&#xff0c;我才意识到&#xff0c;学习Java不仅仅是学习一门编程语言&#xff0c;更是一种思维方式和解决问题的能力的培养…

线程安全问题的原因和解决方案

一.线程安全问题原因 1.根本原因:操作系统的线程是"抢占式执行,随机调度",随机调度是一个程序在多线程环境下,执行顺序存在很多的变数, 2.多个线程同时修改同一个变量 如count这个操作&#xff0c;如果两个线程同时执行&#xff0c;就有可能出现两个线程同时读取co…

3分钟了解Android中稳定性测试

一、什么是Monkey Monkey在英文里的含义是猴子&#xff0c;在测试行业的学名叫“猴子测试”&#xff0c;指的是没有测试经验的人甚至是根本不懂计算机的人&#xff08;就像一只猴子&#xff09;&#xff0c;不需要知道程序的任何用户交互方面的知识&#xff0c;给他一个程序&a…

解决MAC连上wifi或热点却不能上网问题

解决MAC连上wifi或热点却不能上网问题 #新换的mac昨天还能连上wifi&#xff0c;今天就不好使了。 找到连接的wifi点击详细信息&#xff0c;选择TCP/IP 中的配置IPV4 选择关闭

STM32-开发工具

开发过程中可能用到的工具 1、烧录下载调试工具ST-LINK ST-LINK&#xff0c;是ST(意法半导体)推出的调试编程工具&#xff0c;适用于STM32系列芯片的USB接口的下载及在线仿真器。 2、串口调试工具/串口下载工具 串口调试工具是一种用于通过串口通信协议与目标设备进行数据交…

Maxwell安装部署

1 Maxwell输出格式 database&#xff1a;变更数据所属的数据库table&#xff1a;变更数据所属的表type&#xff1a;数据变更类型ts&#xff1a;数据变更发生的时间xid&#xff1a;事务idcommit&#xff1a;事务提交标志&#xff0c;可用于重新组装事务data&#xff1a;对于inse…

Kettle如何连接SQL Server和问题处理

简介 Kettle&#xff08;也称为 Pentaho Data Integration&#xff09;是一款开源的 ETL&#xff08;Extract, Transform, Load&#xff09;工具&#xff0c;由 Pentaho 开发。ETL 是指从一个数据源&#xff08;通常是数据库&#xff09;中提取数据&#xff0c;进行转换&#…

NS安装-CentOS服务器安装Nightscout CGM

NS CGM 安装必要条件 有自己的云服务器好像没有2&#xff0c;有云服务器就行了 安装顺序 先安装数据库&#xff0c;目前支持的是 MongoDB &#xff0c;官方推荐4&#xff0c;其实目前最新版本就行。可以用宝塔安装&#xff0c;比较简单克隆代码&#xff0c;我是放到 /opt/ns…

面试redis篇-03缓存击穿

原理 缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮 解决方案一:互斥锁 解决方案二:逻辑过期 提问与回答 面试官 :什么是缓存击穿 ? 怎么解决 ? 回答: 缓存击穿的意思…

OpenCV库及在ROS中使用

OpenCV库及在ROS中使用 依赖 cv_bridge image_transport roscpp rospy sensor_msgs std_msgsCMakeLists.txt添加 find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) target_link_libraries(pub_img_topic ${catkin_LIBRARIES} ${Opencv_LIBS}) C …

高效办公工具:多微信批量自动添加好友

在现代社会&#xff0c;微信已经成为人们日常工作和生活中必不可少的沟通工具。对于销售人员或者市场推广人员来说&#xff0c;添加好友是非常重要的一项工作&#xff0c;但是手动逐个添加好友显然效率太低。因此&#xff0c;我们可以借助微信管理系统的功能&#xff0c;进行多…

【小记】MacOS Install golang

问题 - command not found: go ➜ brew install golang ➜ go version go version go1.21.7 darwin/arm64写在最后&#xff1a;若本文章对您有帮助&#xff0c;请点个赞啦 ٩(๑•̀ω•́๑)۶

OpenCV 4基础篇| 色彩空间类型转换

目录 1. 色彩空间基础2. 色彩空间类型2.1 GRAY 色彩空间2.2 BGR 色彩空间2.3 CMY(K) 色彩空间2.4 XYZ 色彩空间2.5 HSV 色彩空间2.6 HLS 色彩空间2.7 CIEL*a*b* 色彩空间2.8 CIEL*u*v* 色彩空间2.9 YCrCb 色彩空间 3. 类型转换函数3.1 cv2.cvtColor3.2 cv2.inRange 1. 色彩空间…

如何减少 HTTP 响应的数据大小

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 如何减少 HTTP 响应的数据大小? 对于 HTTP 的请求和响应&#xff0c;通常 HTTP 的响应的数据大小会比较大&#xff0c;也就是服务器返回的资源会比较大。 于是&#xff0c;我们可以考虑对响应的资源进…

JMeter性能测试系列一初识JMeter

1.JMeter介绍 Apache组织的Stefano Mazzocchi是JMeter项目的创始人。编写JMeter最初的目的是为了测试server的性能(后期被Tomcat替代)。随后&#xff0c;JMeter在Apache组织内部开始被其他项目所使用&#xff0c;并最终推广出来&#xff0c;成为独立的软件项目并不断更新&…

C语言第二十四弹---指针(八)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、数组和指针笔试题解析 1.1、字符数组 1.1.1、代码1&#xff1a; 1.1.2、代码2&#xff1a; 1.1.3、代码3&#xff1a; 1.1.4、代码4&#xff1a; 1…

安全防御-第五次

新建NAT策略 新建NAT策略 双机热备 FW1 FW3 新建带宽策略 办公区限流

c编译器学习02:chibicc文档翻译

目的 先粗略地看一遍作者的书籍。 原文档地址 https://www.sigbus.info/compilerbook# “低レイヤを知りたい人のためのCコンパイラ作成入門” 为想了解底层的人准备的C编译器制作入门 Rui Ueyama ruiucs.stanford.edu 2020-03-16 作者简介 https://www.sigbus.info/ 植山…
推荐文章