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

news2024/11/17 13:22:08

 

目录

 前言:

 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数组

 

如果此时我们动态规划到红色的这块区域,由dp数组的公式 dp[i][j]= max(dp[ i-1] [ j ],dp [ i-1 ][ j-weight[i]] + value[i])我们可以知道,这块红色的区域的值一定是由整个数组的左上角区域慢慢推过来的。因此在开始我们就要把左上角的全部初始化,防止出现减不了的情况:

 

 而具体的初始化值,我们简单想一想就可以知道,当背包容量为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背包,其实我们可以发现动态规划题目还是有比较强的套路性的,我们把动态规划拆分成为了五部,我们只要按照这五步进行,实际上解决动态规划题目还是很简单的。

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

 

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

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

相关文章

linux下性能分析工具Perf安装与用法

目录 1、Perf介绍 2、火焰图分类 &#xff08;1&#xff09;CPU &#xff08;2&#xff09;Memory Flame Graphs &#xff08;3&#xff09;Off-CPU Flame Graphs &#xff08;4&#xff09;Hot/Cold Flame Graphs &#xff08;5&#xff09;Differential 3、火焰图安装命令 …

【项目 线程 1】 3.1线程概述 3.2创建线程 3.3终止线程 3.4连接已终止的线程

文章目录 3.1线程概述线程概述线程和进程区别线程和进程虚拟地址空间线程之间共享和非共享资源NPTL 3.2 创建线程线程操作创建线程出现报错及原因 3.3终止线程3.4连接已终止的线程 3.1线程概述 线程概述 并发&#xff1a;两队人用同一个咖啡机&#xff08;本质上同一时刻只有…

算法竞赛入门【码蹄集新手村600题】(MT1100-1120)C语言

算法竞赛入门【码蹄集新手村600题】(MT1100-1120&#xff09;C语言 目录MT1101 带参数的宏IIMT1102 长方体MT1103 球体积MT1104 三角形MT1105 英寸英尺英里MT1106 盎司磅MT1107 加仑/升MT1108 保留小数MT1109 和10相比MT1110 最小值MT1111 最大值MT1112 中庸之道MT1113 三人同行…

这可是全网最全的网络工程师零基础实战视频整理,最新版分享

互联网中每一项傍身的技能都是需要从如何入门开始的&#xff0c;网络技术也是如此&#xff01; 网络技术区别其他互联网技能的一点是学习需要从设备开始&#xff0c;只有认识了解了路由器、交换机、防火墙这些网络设备&#xff0c;才开始从网络通信原理开始&#xff0c;这使得网…

数据安全与可追溯:PDM系统的信息保护锦囊

在当今数字化时代&#xff0c;数据安全与可追溯是企业管理中至关重要的环节。PDM系统&#xff08;Product Data Management&#xff0c;产品数据管理&#xff09;作为一款强大的数字化工具&#xff0c;为企业提供了全方位的信息保护锦囊。让我们一同深入探讨&#xff0c;看看PD…

k8s手动发布镜像的方法

kubectl edit deploy编辑对应的文件&#xff0c;并:wq!保存即可

STL C++学习背景

STL C学习背景 背景知识 背景知识 STL前置知识 STL&#xff0c;英文全称 standard template library&#xff0c;中文可译为标准模板库或者泛型库&#xff0c;其包含有大量的模板类和模板函数&#xff0c;是 C 提供的一个基础模板的集合&#xff0c;用于完成诸如输入/输出、数…

2023新款MacBook Pro 14效果如何

新款MacBook Pro 14 显示屏背面依然保持着苹果LOGO&#xff0c;而且比Air大一圈&#xff0c;看来贵是有道理的&#xff0c;LOGO都变大了&#xff01;该机配件是一个67W的充电头&#xff0c;以及MagSafe 3的充电线。而机身金属感十足&#xff0c;上手体验&#xff0c;确实有万元…

Java阶段五Day21

Java阶段五Day21 文章目录 Java阶段五Day21问题解析rocketmq清空数据 linux学习背景什么是linux系统虚拟机介绍启动 虚拟机linux虚拟机网络的问题 linux系统的基础命令命令提示符命令格式pwd指令ls指令cd指令mkdirtouch指令cp指令rm指令mv指令cat指令tail指令 文本编辑器vim操作…

代码随想录算法训练营第八天| 28.找到字符串中第一个匹配项的下标,459.重复的子字符串(二刷三刷)

28. 找出字符串中第一个匹配项的下标 28.找到字符串中第一个匹配项的下标 KMP算法 原串&#xff1a;sadbutsad 匹配串&#xff1a;sad 构造next数组其实就是计算模式串s的前缀表的过程。与原串是无关的 关于最长公共前缀和最长公共后缀&#xff1a; 前缀是指不包含最后一…

cmake配置Qt工程

cmake 工程配置 # 指定版本和项目 cmake_minimum_required(VERSION 3.10) set(TARGET_NAME labelDeviceView) project(${TARGET_NAME} ) include(${CMAKE_CURRENT_LIST_DIR}/../../../../../../ossLib/ossLib/env.cmake) set(CMAKE_PREFIX_PATH "D:/Qt6/6.5.2/msvc2019…

进程复制fork详解 僵尸进程 孤儿进程 写时拷贝技术 缓冲区

fork函数讲解 进程复制fork基本使用简单分页 逻辑页 物理页 页表fork的三个面试练习题缓冲区僵死进程孤儿进程写时拷贝 进程替换exexc 介绍简易命令解释器strtok()函数讲解 进程复制 fork基本使用 父进程fork后&#xff0c;新的进程产生&#xff0c;新的进程就继续从fork往后的…

Spring集成Web

目录 1、简介 2、监听器 3、Spring提供的listener 3.1、xml 3.2、配置类 3.3、WebApplicationContextUtils 3.4、说明 4、自己复现的listener 4.1、ContextLoaderListener 4.2、WebApplicationContextUtils 4.3、Web调用 ⭐作者介绍&#xff1a;大二本科网络工程专业…

Less is More: Focus Attention for Efficient DETR

摘要 类似detr的模型显著提高了探测器的性能&#xff0c;甚至优于经典的卷积模型。然而&#xff0c;在传统的编码器结构中&#xff0c;所有的标记都带来了冗余的计算负担。最近的稀疏化策略利用了信息标记的一个子集&#xff0c;通过稀疏编码器来降低注意力的复杂性&#xff0…

观测,让运维更简单!龙蜥社区系统运维 MeetUp 等你报名

为了让广大社区用户和开发者近距离感受 Linux 和 eBPF 技术的魅力&#xff0c;龙蜥社区系统运维于 08 月 12 日在杭州举行 MeetUp 。过去&#xff0c;系统运维 SIG 和大家一起交流了 eBPF 在安全和网络的最佳应用和实践&#xff0c;以及 Linux 在手机和服务器上的运维经验等技术…

深度学习训练营之CGAN生成手势图像

深度学习训练营之CGAN生成手势 原文链接CGAN简单介绍环境介绍前置工作数据导入所需的包加载数据创建数据集查看数据集 模型设置初始化模型的权重定义生成器构造判别器 模型训练定义损失函数设置超参数正式开始训练 结果可视化 原文链接 &#x1f368; 本文为&#x1f517;365天…

leetcode 763. 划分字母区间

2023.8.3 本题的关键是要确保同一字母需要在同一片段中&#xff0c;而这就需要关注到每个字母最后一次出现的位置。 思路&#xff1a;用一个哈希表保存每个字母&#xff08;26个&#xff09;最后一次出现的位置。然后从头遍历&#xff0c;不断更新最右边界&#xff0c;直到当前…

LLVM笔记1

参考&#xff1a;https://www.bilibili.com/video/BV1D84y1y73v/?share_sourcecopy_web&vd_sourcefc187607fc6ec6bbd2c74a3d0d7484cf 文章目录 零、入门名词解释1. Compiler & Interpreter2. AOT静态编译和JIT动态解释的编译方式3. Pass4. Intermediate Representatio…

Eureka增加账号密码认证登录

一、业务背景 注册中心Eureka在微服务开发中经常使用到&#xff0c;用来管理发布的微服务&#xff0c;供前端或者外部调用。但是如果放到生产环境&#xff0c;我们直接通过URL访问的话&#xff0c;这显然是不安全的。 所以需要给注册中心加上登录认证。 通过账号和密码认证进行…

openGauss学习笔记-30 openGauss 高级数据管理-别名

文章目录 openGauss学习笔记-30 openGauss 高级数据管理-别名30.1 语法格式30.1.1 列别名语法30.1.2 表别名语法 30.2 参数说明30.3 示例 openGauss学习笔记-30 openGauss 高级数据管理-别名 SQL可以重命名一张表或者一个字段的名称&#xff0c;这个名称为该表或该字段的别名。…