【夜深人静学习数据结构与算法 | 第十二篇】动态规划——背包问题

这篇具有很好参考价值的文章主要介绍了【夜深人静学习数据结构与算法 | 第十二篇】动态规划——背包问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【夜深人静学习数据结构与算法 | 第十二篇】动态规划——背包问题,【夜深人静学数据结构与算法】,leetcode,算法,职场和发展,背包问题 

目录

 前言:

 01背包问题:

二维数组思路:

一维数组思路:

总结:


 前言:

      在前面我们学习动态规划理论知识的时候,我就讲过要介绍一下背包问题,那么今天我们就来讲解一下背包问题。

在这里我们只介绍01背包,至于分组背包和混合背包这种的已经属于竞赛级别的了,难度过高,我们在这里就不学习了。

【夜深人静学数据结构与算法 | 第十篇】动态规划_我是一盘牛肉的博客-CSDN博客

 01背包问题:

该问题的背景是一个背包和一组物品,每个物品都有自己的价值和重量。目标是选择一些物品放入背包中,使得放入的物品总重量不超过背包的容量,且总价值最大化。

具体来说,给定 n 个物品,其重量分别为 w1, w2, …, wn,价值分别为 v1, v2, …, vn,以及一个背包的容量 W。如何在不超过背包容量的情况下拿到的物品可以实现价值最大?

我们还是严格按照动态规划五步走来确定解题思路:

二维数组思路:

1.dp数组的含义以及下标的含义:dp[i][j]的含义为把[0,i]的物品放到容量为j的背包里 的最大价值。

  • 如果不放当前第 i 个物品,那么此时的最大价值就是 dp[ i-1] [ j ]
  • 如果放当前第 i 个物品,那么此时的最大价值就是 dp [ i-1 ][ j-weight[i]] + value[i]

2.递推公式的推导:dp[i][j]= max(dp[ i-1] [ j ],dp [ i-1 ][ j-weight[i]] + value[i])

3.dp数组的初始化:对于dp数组应该如何初始化,我们可以用画图的方式来表示一下dp数组

【夜深人静学习数据结构与算法 | 第十二篇】动态规划——背包问题,【夜深人静学数据结构与算法】,leetcode,算法,职场和发展,背包问题

 文章来源地址https://www.toymoban.com/news/detail-650612.html

如果此时我们动态规划到红色的这块区域,由dp数组的公式 dp[i][j]= max(dp[ i-1] [ j ],dp [ i-1 ][ j-weight[i]] + value[i])我们可以知道,这块红色的区域的值一定是由整个数组的左上角区域慢慢推过来的。因此在开始我们就要把左上角的全部初始化,防止出现减不了的情况:
【夜深人静学习数据结构与算法 | 第十二篇】动态规划——背包问题,【夜深人静学数据结构与算法】,leetcode,算法,职场和发展,背包问题

 

 而具体的初始化值,我们简单想一想就可以知道,当背包容量为0的时候,就装不了东西,那么最大价值就是0,那么我们就把竖行的值初始化为0,也就是dp[i][0]初始化为0,横行就是始终装物品0,那么只要背包的容量大于物品0的容量,最大的价值就是dp[0][j]=value.(物品0)。

4.dp数组遍历顺序:对于二维数组的这两个for循环,无论是先便利背包还是物品都是可以的。

那么用一个例子来实现一下动态规划:

#include <iostream>
#include <vector>

using namespace std;

// 定义物品结构体,包含重量和价值
struct Item {
    int weight;
    int value;
};

int knapsack(int W, vector<Item>& items) {
      int size = items.size();
      vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= W; j++) {
            // 当前物品重量大于背包容量,无法放入背包
            if (items[i - 1].weight > j) {
                dp[i][j] = dp[i - 1][j];
            }
            else {
                // 考虑放入或不放入当前物品的两种情况,取最大值
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - items[i - 1].weight] + items[i - 1].value);
            }
        }
    }

    return dp[n][W]; // 返回最优解
}

int main() {
    int W = 10; // 背包容量

    vector<Item> items = { {2, 6}, {2, 10}, {3, 12} }; // 物品列表

    int maxValue = knapsack(W, items); // 求解最优解

    cout << "最大总价值为: " << maxValue << endl;

    return 0;
}

一维数组思路:

在一维数组优化中,我们只需要创建一个长度为背包容量+1的一维数组,用于记录在不超过当前背包容量下的最优解。具体优化过程如下:

原始的二维dp数组定义为dp[n + 1][W + 1],其中dp[i][j]表示在前i个物品中选择不超过重量j的物品时的最优解。

将二维dp数组优化为一维数组dp[W + 1],其中dp[j]表示在不超过背包容量j的情况下的最优解。

优化后的代码示例:

#include <iostream>
#include <vector>

using namespace std;

// 定义物品结构体,包含重量和价值
struct Item {
    int weight;
    int value;
};

int knapsack(int W, vector<Item>& items) {
    int n = items.size();

    // 创建一维dp数组并初始化为0
    vector<int> dp(W + 1, 0);

    for (int i = 0; i < n; i++) {
        for (int j = W; j >= items[i].weight; j--) {
            // 考虑放入或不放入当前物品的两种情况,取最大值
            dp[j] = max(dp[j], dp[j - items[i].weight] + items[i].value);
        }
    }

    return dp[W]; // 返回最优解
}

int main() {
    int W = 10; // 背包容量

    vector<Item> items = { {2, 6}, {2, 10}, {3, 12} }; // 物品列表

    int maxValue = knapsack(W, items); // 求解最优解

    cout << "最大总价值为: " << maxValue << endl;

    return 0;
}

在上述代码中,我们使用一个长度为背包容量+1的一维数组dp[W + 1]来记录在不超过当前背包容量下的最优解。在计算时,我们从后往前遍历物品,并从后往前更新一维dp数组。这样可以确保在更新dp[j]时,所需的dp[j - items[i].weight]已经是前一轮的值,并且不会影响当前轮的计算结果。通过这种方式,可以实现将二维dp数组优化为一维数组的目的,并得到正确的最优解。

总结:

        本文我们学习了01背包,其实我们可以发现动态规划题目还是有比较强的套路性的,我们把动态规划拆分成为了五部,我们只要按照这五步进行,实际上解决动态规划题目还是很简单的。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

【夜深人静学习数据结构与算法 | 第十二篇】动态规划——背包问题,【夜深人静学数据结构与算法】,leetcode,算法,职场和发展,背包问题

 

到了这里,关于【夜深人静学习数据结构与算法 | 第十二篇】动态规划——背包问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【夜深人静学数据结构与算法 | 第三篇】 二叉树

    目录  前言:  二叉树: 二叉树的种类:  二叉树的存储方式: 1. 链式存储 2. 数组存储 二叉树的遍历方式 深度优先遍历 广度优先遍历 总结: 本文将会详细的介绍各种常见的树以及相对应的概念,存储方式,遍历方式。树是经典的数据结构,因此我们虽然不能做到手撕各

    2024年02月11日
    浏览(36)
  • 【夜深人静学数据结构与算法 | 第十篇】动态规划

    目录 前言: 动态规划: 常见应用: 解题步骤:  动态规划的简化步骤: 案例: 509. 斐波那契数 - 力扣(LeetCode) 70. 爬楼梯 - 力扣(LeetCode) 62. 不同路径 - 力扣(LeetCode) 总结:         本文我们将为大家讲解一下动态规划的理论知识,并且会讲解几道力扣的经典例题。

    2024年02月11日
    浏览(45)
  • 【夜深人静学数据结构与算法 | 第九篇】栈与队列

    目录 ​前言: 栈: 栈的实际应用:  队列: 队列的实际应用: 总结:         栈与队列是我们学习的两个经典的数据结构,这两个数据结构应用广泛,在计算机内有很多底层应用,而很多算法也是依靠栈和队列来实现的,因此我们要想学好数据结构与算法,就要学好栈与

    2024年02月15日
    浏览(33)
  • 【夜深人静学数据结构与算法 | 第二篇】后缀(逆波兰)表达式

    目录 前言:  中缀表达式:  后缀表达式: 中缀表达式转后缀表达式: 后缀表达式计算结果: 总结:  计算机在计算四则运算的时候,由于括号以及运算优先级的存在,并不能够很好的处理所有的运算,为了处理这种情况,我们引入了后缀表达式来优化算法。         

    2024年02月13日
    浏览(37)
  • 【夜深人静学数据结构与算法 | 第四篇】手撕二叉树遍历

    目录 前言: 二叉树遍历方式: 手撕前中后序遍历(递归)的三大准备 深度优先搜索:  手撕前中后遍历(递归): 手撕前中后序遍历(迭代): 深度优先搜索: 总结:         今天我们将带领大家手撕二叉树的遍历,本篇会分别讲解深度优先搜索法和广度优先有搜索法下

    2024年02月09日
    浏览(34)
  • 【夜深人静学数据结构与算法 | 第七篇】时间复杂度与空间复杂度

    前言:  引入:  时间复杂度:  案例: 空间复杂度: 案例: TIPS:        总结:         今天我们将来介绍时间复杂度和空间复杂度,我们代码的优劣就是依靠这个在评判,以此为背景,我们诞生出了不少的经典思路:用时间换空间,用空间换取时间。而大多数同学

    2024年02月10日
    浏览(49)
  • 夜深人静学32系列10——GPIO中断/NVIC/EXTI/SYSCFG详解,外部中断控制LED

    上期我们学习了GPIO驱动数码管/蜂鸣器/LED和按键等外设,本期我们一起来学习STM32中断的相关内容 当CPU正在处理某个事件的时候,外界发生了紧急事件请求,CPU需要暂停当前的工作,转而去处理这个紧急事件,处理完之后,再次回到之前被中断的地方,继续执行原来的工作,

    2024年01月16日
    浏览(32)
  • 数据结构与算法学习(day1)

    (1)我是一个大三的学生(准确来说应该是准大三,因为明天才报名哈哈哈)。 (2)最近就想每天闲着没事也刷些C语言习题来锻炼下编程水平,也一直在思考企业对应届大学生能力的要求,所以经常会想到关于面试的事情。由于我也没实习过,所以我对面试没有一个具象化

    2024年02月10日
    浏览(39)
  • 学习数据结构和算法的第9天

    移除元素 ​ 给你一个数组 nums 和一个值 val ,你需要 原地 移除所有数值等于 val的元素,并返回移除后数组的新长度。 ​ 不要使用额外的数组空间,你必须仅使用 0(1)额外空间并 原地 修改输入数组。 ​ 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

    2024年02月21日
    浏览(26)
  • 学习数据结构和算法的第8天

    进行头插 eg:在数组 1 2 3 4 5的开头插入-1 变成-1 1 2 3 4 5 头删 eg:数组1 2 3 4 5 删去1

    2024年02月22日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包