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

难度参考

        难度:困难

        分类:动态规划

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

题目

        动态规划经典问题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.mzph.cn/news/688369.shtml

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

相关文章

Shiro反弹shell和权限绕过含工具包

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 1、前言 反序列化漏洞原理和Shiro反序列化漏洞原理请参…

如何使用Sora?Sora小白教程一文通

如果需要使用Sora或者GPT4&#xff0c;请参考文章&#xff1a;如何使用Sora&#xff1f;Sora小白教程一文通 什么是Sora Sora是OpenAI于2024年2月18日凌晨发布的新的文生视频大模型&#xff0c;名为 “ Sora ”。 从OpenAI在官网展示的Sora生成视频的效果来看&#xff0c;在生…

day34打卡

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

软件实例分享,台球厅收费系统电脑桌球店计时软件及灯控线路图教程

软件实例分享&#xff0c;台球厅收费系统电脑桌球店计时软件及灯控线路图教程 一、前言 以下软件程序教程以 佳易王桌球室计时计费软件V17.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、每个桌子对应的有 开台时间&#xff0c;所用的时…

python如何模拟登录Github

首先进入github登录页&#xff1a;https://github.com/login 输入账号密码&#xff0c;打开开发者工具&#xff0c;在Network页勾选上Preserve Log&#xff08;显示持续日志&#xff09;&#xff0c;点击登录&#xff0c;查看Session请求&#xff0c;找到其请求的URL与Form Da…

【RL】Monte Carlo Learning(蒙特卡洛学习)

Lecture 5: Monte Carlo Learning The simplest MC-based RL algorithm: MC Basic 理解MC basic算法的关键是理解如何将policy iteration算法迁移到model-free的条件下。 Policy iteration算法在每次迭代过程中有两步&#xff1a; { Policy evaluation: v π k r π k γ…

java之VO,BO,PO,DO,DTO

概念 VO&#xff08;View Object&#xff09;&#xff1a;视图对象&#xff0c;用于展示层&#xff0c;它的作用是把某个指定页面&#xff08;或组件&#xff09;的所有数据封装起来。DTO&#xff08;Data Transfer Object&#xff09;&#xff1a;数据传输对象&#xff0c;这…

VMware虚拟机安装CentOS7

对于系统开发来说&#xff0c;开发者时常会需要涉及到不同的操作系统&#xff0c;比如Windows系统、Mac系统、Linux系统、Chrome OS系统、UNIX操作系统等。由于在同一台计算机上安装多个系统会占据我们大量的存储空间&#xff0c;所以虚拟机概念应运而生。本篇将介绍如何下载安…

趋高技术开发出超低价的视觉尺寸测量仪软件

2024年1月1日元旦节当日&#xff0c;深圳市趋高技术有限公司Fuxi实验室开发组成员成功开发出一款视觉尺寸测量仪软件。这款软件类比市场价格处于超低价。仅报三千二百元。有需要的码农或客户都可以了解一下&#xff0c;带回家。 趋高技术HITREND是深圳的一家高科技公司。 …

Navicat:在 Navicat 中创建外键约束

文章目录 1 表设计2 外键设置3 删除、更新操作设置4 保存 在 Navicat 中&#xff0c;可以在“表设计器”的“外键”选项卡上找到外键约束。 1 表设计 若要创建新的外键约束&#xff0c;请以“表设计器”打开子表&#xff08;在本例中为 fwaq_flow_jcjd&#xff09;&#xff0…

Linux系统的历史记录添加时间和IP信息

1 为什么要优化系统历史记录 对于linux系统&#xff0c;默认情况下&#xff0c;系统记录的历史命令比较简单。某些历史记录可能也无法正常保存&#xff0c;因此当服务器出现异常&#xff0c;希望通过历史命令来了解曾经做了哪些操作时&#xff0c;往往非常被动&#xff0c;下面…

[计算机网络]深度学习传输层TCP协议

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录专栏&#xff1a;深度学习传输层TCP协议 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 [计算机网络]深度学习传输层TCP协议 前提概括一: TCP协议段格式二:确认应答三:超时重传四:…

vivado RAM HDL Coding Techniques

Vivado synthesis可以解释各种RAM编码风格&#xff0c;并将它们映射到分布式RAM中或块RAM。此操作执行以下操作&#xff1a; •无需手动实例化RAM基元 •节省时间 •保持HDL源代码的可移植性和可扩展性从编码示例下载编码示例文件。 在分布式RAM和专用RAM之间的选择块存储器…

基于SSM的电影购票系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的电影购票系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spri…

十大经典排序算法之一--------------堆排序(java详解)

一.堆排序基本介绍&#xff1a; 堆排序是利用堆这种数据结构而设计的一种排序算法&#xff0c;堆排序是一种选择排序&#xff0c;它的最坏&#xff0c;最好&#xff0c;平均时间复杂度均为O(nlogn)&#xff0c;它也是不稳定排序。堆是具有以下性质的完全二叉树&#xff1a;每个…

内存基础知识

内存作用&#xff1a;用来存放数据 int x10&#xff1b; xx1&#xff1b; 这会生成一个可执行文件&#xff08;装入模块&#xff09;然后存入内存地址中 绝对装入&#xff1a;-如果知道程序放到内存中哪个位置&#xff0c;编译程序将产生绝对地址的目标代码 可重定位装入&am…

idea 打不开项目 白屏

使用IDEA打开项目&#xff0c; 不知名原因崩溃了&#xff0c; 直接出现缩略图白屏。 解决过程&#xff1a; 尝试过重启IDEA&#xff0c;重启过电脑&#xff0c;重新引入相同项目&#xff08;使用不同路径&#xff0c;存在缓存记录&#xff0c;依然打不开&#xff09;&#xff…

数据结构——lesson3单链表介绍及实现

目录 1.什么是链表&#xff1f; 2.链表的分类 &#xff08;1&#xff09;无头单向非循环链表&#xff1a; &#xff08;2&#xff09;带头双向循环链表&#xff1a; 3.单链表的实现 &#xff08;1&#xff09;单链表的定义 &#xff08;2&#xff09;动态创建节点 &#…

删除链表的倒数第N个节点

删除链表的倒数第N个节点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 进阶&#xff1a;你能尝试使用一趟扫描实现吗&#xff1f; 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例…

蓝桥杯Java组备赛(二)

题目1 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int max Integer.MIN_VALUE;int min Integer.MAX_VALUE;double sum 0;for(int i0;i<n;i) {int x sc.nextInt()…