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

news2025/1/21 0:03:42

难度参考

        难度:困难

        分类:动态规划

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

题目

        动态规划经典问题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(); // 物品数量
    // 初始化动态规划表,所有值起始为0
    vector<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.coloradmin.cn/o/1455079.html

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

相关文章

阿里云香港轻量应用服务器网络线路cn2?

阿里云香港轻量应用服务器是什么线路&#xff1f;不是cn2。 阿里云香港轻量服务器是cn2吗&#xff1f;香港轻量服务器不是cn2。阿腾云atengyun.com正好有一台阿里云轻量应用服务器&#xff0c;通过mtr traceroute测试了一下&#xff0c;最后一跳是202.97开头的ip&#xff0c;1…

RK3399平台开发系列讲解(USB篇)U盘等存储类设备

🚀返回专栏总目录 文章目录 一、什么是U盘等存储类设备二、U盘设备传输数据结构三、U盘识别需要打开的宏沉淀、分享、成长,让自己和他人都能有所收获!😄 📢介绍U盘等存储类设备。 一、什么是U盘等存储类设备 USB Mass Storage Device Class(USB MSC/UMS) USB大容量存…

如何在Linux系统中配置并优化硬盘的RAID

在Linux系统中配置和优化硬盘的RAID技术可以帮助提高数据存储性能和安全性。RAID&#xff08;Redundant Array of Independent Disks&#xff09;技术通过将多个硬盘组合起来&#xff0c;以增加性能、容量或冗余度&#xff0c;提高数据的可靠性和可用性。本文将介绍如何在Linux…

嵌入式学习-C++-Day6

思维导图 作业 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff0c;动物园里有一…

【机构vip教程】Unittest(1):unittest单元测试框架简介

unittest单元测试框架简介 unittest是python内置的单元测试框架&#xff0c;具备编写用例、组 织用例、执行用例、功能&#xff0c;可以结合selenium进行UI自动化测 试&#xff0c;也可以结合appium、requests等模块做其它自动化测试 官方文档&#xff1a;https://docs.pytho…

【计算机网络】P2P应用

将会在两个例子中得出结果 1&#xff0c;对等文件分发 &#xff1b;2&#xff0c;大型对等方社区中的服务器 P2P文件分发 自拓展性 直接成因是&#xff1a;对等方除了是比特的消费者外还是它们的重新分发者BitTorrent 一个用于文件分发的P2P协议洪流 torrent 参与一个特定文件…

day34打卡

day34打卡 860. 柠檬水找零 解法&#xff0c;贪心&#xff1a;局部最优&#xff1a;遇到账单20&#xff0c;优先消耗美元10&#xff0c;完成本次找零 -》全局最优&#xff1a;完成全部账单的找零。 遇到5&#xff0c;直接收下遇到10&#xff0c;找一个5元遇到20&#xff0c;…

CS50x 2024 - Lecture 6 - Python

00:00:00 - Introduction 00:01:01 - Python print("hello world")与c的显著差异 1.不必显式包含标准库 2.不再需要定义main函数 00:07:24 - Speller 00:13:41 - Filter from PIL import Image, ImageFilterbefore Image.open("bridge.jpg") after…

山西电力市场日前价格预测【2024-02-15】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-15&#xff09;山西电力市场全天平均日前电价为168.98元/MWh。其中&#xff0c;最高日前电价为366.42元/MWh&#xff0c;预计出现在18:30。最低日前电价为0.00元/MWh&#xff0c;预计出…

黑马程序员-瑞吉外卖day9

菜品分类下拉列表 CategoryController里面写 /*** 根据条件查询分类数据** param category* return*/GetMapping("/list")ApiOperation("菜品分类目录")public R<List<Category>> list(Category category) {List<Category> list cate…

洛夫克拉夫特“克苏鲁神话”艺术风格探索(一)

克苏鲁神话引入中国时间不长&#xff0c;研究的规模也不如国外大&#xff0c;但克苏鲁神话作为神话史、奇幻小说史上的重要节点&#xff0c;有很大的影响力与非常重要的研究意义。为了促进中文语境下克苏鲁神话元素的使用和创作、更好地设计克苏鲁的呼唤游戏模组&#xff08;剧…

0206-1-网络层

第 4 章 网络层 网络层提供的两种服务 虚电路服务 数据报服务 概要: 虚电路服务与数据报服务的对比 网际协议 IP 网际协议 IP 是 TCP/IP 体系中两个最主要的协议之一。与 IP 协议配套使用的还有四个协议&#xff1a; 地址解析协议 ARP (Address Resolution Protocol)逆地…

【论文精读】SimCLR2

摘要 本文提出了一个半监督学习框架&#xff0c;包括三个步骤&#xff1a;无监督或自监督的预训练&#xff1b;有监督微调&#xff1b;使用未标记数据进行蒸馏。具体改进有&#xff1a; 发现在半监督学习&#xff08;无监督预训练有监督微调&#xff09;中&#xff0c;对于较大…

嵌入式day24

开课复工啦~ 冲冲冲&#xff01; 文件IO&#xff1a; read函数和write函数&#xff1a; &#x1f4da; write 接口有三个参数&#xff1a; fd&#xff1a;文件描述符buf&#xff1a;要写入的缓冲区的起始地址&#xff08;如果是字符串&#xff0c;那么就是字符串的起始地址&…

语义分割-基础知识

1.cls_iou计算: cls0_iou预测正确的像素个数/&#xff08;预测为该类别的像素个数真实标签为该类别的像素个数-预测正确的像素个数&#xff09; mean_iou各个类别的像素预测准确值相加/像素总个数2.转置卷积(Transposed Convolution) 转置卷积不是卷积的逆运算 转置卷积也是卷…

黑猫带你学NandFlash第2篇:NandFlash部分相关名词释义

1 前言 1.1 声明 本文依据ONFI5.1、网络资料及个人工作经验整理而成&#xff0c;如有错误请留言。 文章为付费内容&#xff0c;已加入原创侵权保护&#xff0c;禁止私自转载及抄袭。 文章所在专栏&#xff1a;《黑猫带你学&#xff1a;NandFlash详解》 1.2 本文背景 本文关…

springboot198基于springboot的智能家居系统

基于Springboot的智能家居系统 **[摘要]**社会和科技的不断进步带来更便利的生活&#xff0c;计算机技术也越来越平民化。二十一世纪是数据时代&#xff0c;各种信息经过统计分析都可以得到想要的结果&#xff0c;所以也可以更好的为人们工作、生活服务。智能家居是家庭的重要…

【java苍穹外卖项目实战三】nginx反向代理和负载均衡

文章目录 1、nginx反向代理2、nginx 反向代理的好处3、nginx 反向代理的配置方式5、nginx 负载均衡的配置方式6、nginx 负载均衡策略 我们思考一个问题&#xff1a; 前端发送的请求&#xff0c;是如何请求到后端服务的&#xff1f; 前端请求地址&#xff1a;http://localhost/…

【C++学习手札】多态:掌握面向对象编程的动态绑定与继承机制(深入)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;世界上的另一个我 1:02━━━━━━️&#x1f49f;──────── 3:58 &#x1f504; ◀️ ⏸ ▶️ ☰ &am…

手把手教你Linux系统下的Java环境配置,简单到不行!

推荐阅读 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;一&#xff09; 给软件行业带来了春天——揭秘Spring究竟是何方神圣&#xff08;二&#xff09; 文章目录 推荐阅读下载JDK安装包方式一方式二 添加环境变量验证安装情况 下载JDK安装包 方式一 1.进入…