C++ day42背包理论基础01 + 滚动数组

news2024/11/28 10:17:24

背包问题的重中之重是01背包

01背包

有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是o(2^n),这里的n表示物品数量。

所以暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!

举一个例子:背包最大重量为4,有3个物品,其重量和价值如下表

物品为:

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

问背包能背的物品最大价值是多少?

动规五部曲

1)确定dp数组以及下标的含义

对于背包问题,有一种写法, 是使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取放进容量为j的背包价值总和最大是多少,i代表第几个物品,j代表背包的容量,dp[i][j]代表价值

2)递推公式

对于每一个物品都有放与不放两种状态,所以又有两种情况,取这两种情况的最大值

3)dp数组初始化

根据递推公式,可知dp数组是由其左上角和正上方推导而来,所以要对其第一行和第一列进行初始化

其他下标的价值由递推公式更新推导,所以其它地方初始化为任意值均可

4)遍历顺序

因为本题有两个维度,分别为物品维度和背包容量维度,所以到底是先遍历物品呢?还是先遍历背包呢?

可以分类讨论一下

先遍历物品

先遍历背包

5)打印dp数组

代码

int weight_bag(vector<int> weight,vector<int> value,int bagweight){
    vector<vector<int>> dp(weight.size(),vector<int>(bagweight+1,0));
    //初始化.对于物品0
    for(int j=weight[0];j<=bagweight;j++){
        dp[0][j]=value[0];
    }
    //递推(先遍历物品,再遍历背包)
    for(int i=1;i<weight.size();i++){
        for(int j=0;j<=bagweight;j++){
            if(j<weight[i]) dp[i][j]=dp[i-1][j];//如果当前物品重量比背包容量大,则装不下该物品
            //如果当前物品重量比背包容量小,那么可以有两种选择,装下该物品,或是不装该物品
            else dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
        }
    }
    return dp[weight.size()-1][bagweight];
}

01背包(滚动数组)

dp[i - 1]这一层拷贝到dp[i]上,只用一个一维数组dp[j],可以理解是一个滚动数组。

滚动数组需要满足的条件是上一层可以重复利用,直接拷贝到当前层,一直处于一种刷新的状态

动规五部曲

1)确定dp数组的定义

在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。

2)一维dp数组的递推公式

dp[j]有两个选择,一个是不放物品i,取自己dp[j] 相当于 二维dp数组中的dp[i-1][j];一个是放物品i,取dp[j - weight[i]] + value[i],取二者当中的最大值

dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

3)一维dp数组的初始化

dp[0]就应该是0,因为背包容量为0所背的物品的最大价值就是0。

除了下标0的位置,初始为0,其他下标应该初始化多少呢?

根据递推公式,dp数组在推导的时候一定是取价值最大的数,那么非0下标都初始化为0就可以了,这样才能让dp数组在递归公式的过程中取的最大的价值,而不是被初始值覆盖了

4)一维dp数组的遍历顺序

一维dp遍历的时候,背包是从大到小,注意一定要是倒序遍历

倒序遍历是为了保证物品i只被放入一次!。但如果一旦正序遍历了,那么物品0就会被重复加入多次!

因为根据递推公式,每个物品只放一次,在遍历物品的时候,变动j时,每次都要刷新dp[j]

dp[1]=dp[1-weight[0]]+value[0]=dp[0]+15=0+15=15

dp[2]=dp[2-weight[0]]+value[0]=dp[1]+value[0]=15+15=30,这里物品0会放入两次,因为前面刷新了dp[1],初始值会被修改,连带着后面的dp[2]会受到波及!!!!

为什么二维dp数组遍历的时候不用倒序呢?

因为对于二维dp,dp[i][j]都是通过上一层即dp[i - 1][j]计算而来,本层的dp[i][j]并不会被覆盖!,因为是二维数组,没有动态刷新,所以一旦求出数值,便会一直在那保持不变

两个嵌套for循环的顺序,代码中是先遍历物品嵌套遍历背包容量,那可不可以先遍历背包容量嵌套遍历物品呢?

不可以!因为一维dp的写法,背包容量一定是要倒序遍历(原因上面已经写了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品。

因为是每次只对一个背包容量进行求解,

dp[4]=dp[4-1]+value[0]=15

dp[4]=max(15,dp[4-3]+value[1])=max(15,20)=20

dp[4]=max(10,dp[4-4]+value(2))=max(20,30)=30

dp[3]=dp[3-1]+value(0)=value(0)=15

........

如上,因为是倒序遍历和初始化的存在,针对每个背包容量,每次都只放进了一个物品,不能将两个或多个物品的情况考虑在内


dp[4]=dp[4-1]+value[0]=15

dp[3]=max(dp[3],dp[3-1]+value[0])=15

dp[2]=max(dp[2],dp[2-1]+value[0])=15

dp[1]=max(dp[1],dp[1-1]+value[0])=15

dp[4]=max(dp[4],dp[4-3]+value[1])=max(15,15+20)=35

.........

如上,每个背包的容量随着物品的变动与前面每一个物品的情况结合起来,这才是正解

倒序遍历的原因是,本质上还是一个对二维数组的遍历,并且右下角的值依赖上一层左上角的值,因此需要保证左边的值仍然是上一层的,从右向左覆盖。

倾向于使用一维dp数组的写法,比较直观简洁,而且空间复杂度还降了一个数量级

代码

int  weightvalue(int bagweight,vector<int>weight,vector<int>value){
 //初始化dp数组
    vector<int> dp(bagweight+1,0);
    //递推
    for(int i=0;i<weight.size();i++){
        for(int j=bagweight;j>=weight[i];j--){
            dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
        }
    }
    return dp[bagweight];
}

背包问题应用1

题目:416分割等和子集

题目链接:分割等和子集

对题目的理解

非空数组由正整数组成,是否可以将这个数组分割成两个子集,使得两个子集的元素和相等

将问题具象化:只要找到集合里能够出现 sum / 2 的子集总和,就算是可以分割成两个相同元素和子集了。

集合中的元素只能使用一次,因此使用的是01背包

只有确定了如下四点,才能把01背包问题套到本题上来。

  • 背包的容量为sum / 2
  • 背包要放入的商品(集合里的元素)重量为nums[i],价值也为nums[i]
  • 背包如果正好装满,说明找到了总和为 sum / 2 的子集。
  • 背包中每一个元素是不可重复放入

动规五部曲

1)确定dp数组含义及下标

dp[j]:容量为j的背包所背的最大价值为dp[j]

集合中的每个元素既是重量也是价值   dp[target]==target 背包装满 其中target=sum/2

2)递推公式

背包的递推公式:dp[j]=max(dp[j],dp[j-weight[i]]+value[i])

所以本题的递推公式:dp[j]=max(dp[j],dp[j-nums[i]]+nums[i])

3)dp数组初始化

dp[0]=0   ,非零下标进行初始化,dp[i]=0,根据递推公式,不能初始化别的值,初始化为非负整数的最小值,即0,这样才能让dp数组在递推的过程中取得最大的价值,而不是被初始值覆盖了

4)遍历顺序

遍历顺序:先正序遍历物品,后倒序遍历背包容量

for(i=0;i<nums.size(i++)){

     for(j=target;j>=nums[i];j--)}

5)打印dp数组

代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        vector<int> dp(10001,0);//定义dp数组,并将其初始化为0,因为数组中的元素最大是100,且至少有200个数那么最大的重量为200*100=20000,背包中只有一半容量就可以了
        int sum = 0;
        for(int i=0;i<nums.size();i++){
            sum += nums[i];
        }
        if(sum % 2==1) return false;//如果和的一半是奇数,那么不可能有两个子集和相等
        int target = sum/2;
        //递推公式
        //先正序遍历物品,后倒序遍历背包
        for(int i=0;i<nums.size();i++){
            for(int j=target;j>=nums[i];j--){
                dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        if(dp[target]==target) return true;
        return false;
    }
};

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n),虽然dp数组大小为一个常数,但是大常数

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

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

相关文章

AI技术如何助力实现智慧交通

人工智能的常见优势在于能够实时、高效地分析处理大量的数据&#xff0c;并结合算法模型提供个性化、专业化的服务。在智慧交通方面&#xff0c;人工智能同样可以发挥专长&#xff0c;助力打造智能高效的交通运输网络&#xff0c;本篇就为大家简单介绍一下AI技术如何促进智慧交…

多平台小程序编译适配,是否会让更多App互联互通?

随着科技的飞速发展&#xff0c;我们正迅速进入一个以数字化为主导的时代。 在这个时代中&#xff0c;通信、小程序、快应用、云服务器等平台连接类软件如火如荼的发展&#xff0c;手机、手表、AR/VR眼镜等智能移动穿戴设备迅速的升级迭代&#xff0c;5G、芯片、算力等基础设施…

Python财经股票数据保存表格文件 <雪球网>

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 环境使用: Python 3.10 解释器 Pycharm 编辑器 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都准备好了&…

天软高频时序数据仓库

1天软高频时序数仓方案架构 天软高频时序数据仓库是深圳天软科技开发有限公司专为金融用户提供的专业高频行情数据处理方案&#xff0c;集数据接入、检查、处理、存储、查询、订阅、计算于一体。 方案支持各类系统的实时行情、非实时行情接入&#xff1b;还支持压缩存储、分布式…

Kubernetes Dashboard 涉及的一些常规技巧

Kubernetes Dashboard 提供了一个GUI形式的K8S集群管理工具&#xff0c;通过它我们能很容易的观察到集群资源消耗情况、服务器运行状态以及针对Pod的相关观察与操作&#xff1b; Dashboard 的相关配置 Dashboard 提供了通过配置启动命令行参数来控制其相关行为的能力&#xf…

知识工作者,需要填报工时么? | IDCF

作者&#xff1a;冬哥 来源&#xff1a;DevOps 引 子 “知识工作者&#xff0c;需要填报工时么&#xff1f;”忘记是因为什么&#xff0c;突然想到这个话题。似乎是没什么值得讨论的话题&#xff0c;我们的观点也是旗帜鲜明地认为没有必要&#xff0c;但实际现实中填报工时似…

游戏开发原画的设计方法

游戏原画设计是游戏开发中至关重要的一环&#xff0c;因为它直接影响到游戏的视觉吸引力和用户体验。以下是一些常见的游戏原画设计方法&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 理解游戏概念&…

如何用CHAT写“科技探索者”视频号运营方案

问CHAT&#xff1a;生成一篇“科技探索者”视频号运营方案&#xff0c;要求内容&#xff1a; &#xff08;1&#xff09;视频号的定位、面向的人群、主要发布哪方面的内容 &#xff08;2&#xff09;视频号的内容设计&#xff08;用什么样的方式来体现、最好有内容创意&#xf…

会议预告 | 求臻医学受邀参加2023·Inno China 产业创新大会

INNO CHINA 中国产业创新大会聚焦于数据驱动产业变革升级、医疗科技与产业转型升级、企业数字化转型升级、产业服务生态构建及商业智能融合发展等领域。如今&#xff0c;已成为中国新兴科技、热门赛道行业论坛、创新成果展示、参与、共创的高维度学术与产业年度相聚的节日&…

CRM系统是怎样帮助销售流程自动化的?

销售业绩是衡量企业经营的重要指标&#xff0c;也是销售人员一直要达成的目标。销售业绩能否提高取决于销售人员的能力、客户服务水平&#xff0c;还需要借助有效的工具。CRM系统就是这样的一款软件。企业如何提高销售业绩&#xff1f;不妨试试CRM销售流程自动化。 CRM如何实现…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于世界模型深度强化学习的含风电电力系统低碳经济调度》

这个标题表明研究的主题涉及到一个电力系统&#xff0c;该系统包含风电&#xff0c;并且使用基于世界模型的深度强化学习进行低碳经济调度。以下是对标题中各个关键词的解读&#xff1a; 基于世界模型&#xff1a; 这可能指的是使用一种模型来表示电力系统所在的环境&#xff0…

使用mock.js模拟数据

一、安装mock.js npm i mockjs 二、配置JSON文件 我们创建一个mock文件夹&#xff0c;用于存放mock相关的模拟数据和代码实现。 我们将数据全部放在xxx.json文件夹下&#xff0c;里面配置我们需要的JSON格式的数据。 注意&#xff1a;json文件中不要留有空格&#xff0c;否则…

如何使用 ONLYOFFICE 文档代理功能

简介 ONLYOFFICE 文档以在线应用程序的方式运行&#xff0c;在很多情形中可能会存在需要将其集成至内部网络的情形。如今&#xff0c;许多内部网络维护者可能会出于某些目的使用不同的 Web 服务器作为代理。此时ONLYOFFICE 文档中的代理功能就能派上用场了。市面上应用最广泛的…

交流负载的功能实现原理

交流负载的功能实现原理主要涉及到电力电子技术、电机控制技术和电力系统保护技术等多个方面。 交流负载的功能实现需要通过电力电子器件进行电能的转换和控制&#xff0c;电力电子器件主要包括开关器件和电力电子变压器等。开关器件主要用于实现电能的通断控制&#xff0c;如晶…

【shell】文本三剑客之sed详解

目录 一、sed简介&#xff08;行编辑器&#xff09; 二、基本用法 三、sed脚本格式&#xff08;匹配地址 脚本命令&#xff09; 1、不给地址&#xff0c;那么就是针对全文处理 2、单地址&#xff0c;表示#&#xff0c;指定的行&#xff0c;$表示最后一行&#xff0c;/pattt…

将图像的rgb数据转成DICOM医学图像格式

dcmtk官方文档&#xff1a;https://support.dcmtk.org/docs/ dcmtk最新源码下载&#xff1a;https://www.dcmtk.org/en/dcmtk/dcmtk-software-development/ dcmtk旧版本源码下载&#xff1a;https://dicom.offis.de/download/dcmtk/ 用DCMTK库实现将图像转成dcm格式 dcmtk库的…

常见面试题-Redis 切片集群以及主节点选举机制

Redis 切片集群了解吗&#xff1f; 答&#xff1a; Redis 切片集群是目前使用比较多的方案&#xff0c;Redis 切面集群支持多个主从集群进行横向扩容&#xff0c;架构如下&#xff1a; 使用切片集群有什么好处&#xff1f; 提升 Redis 读写性能&#xff0c;之前的主从模式中&…

构建强大的接口自动化测试框架:Pytest实践指南!

一. 背景 Pytest目前已经成为Python系自动化测试必学必备的一个框架&#xff0c;网上也有很多的文章讲述相关的知识。最近自己也抽时间梳理了一份pytest接口自动化测试框架&#xff0c;因此准备写文章记录一下&#xff0c;做到尽量简单通俗易懂&#xff0c;当然前提是基本的py…

系列二十四、Spring设计模式之策略模式

一、前言 对于我们Java开发人员来说&#xff0c;Spring框架的重要性不言而喻&#xff0c;可以说Java领域之所以发展这么壮大&#xff0c;生态这么丰富&#xff0c;功能这么强大&#xff0c;是离不开Spring以及由其衍生出来的各种子模块的&#xff0c;正是由它们共同奠定了JavaE…

波司登发布2023/24上半财年业绩:稳健高质量增长,旺季业绩可期

11月27日&#xff0c;羽绒服巨头波司登&#xff08;03998.HK&#xff09;正式发布 2023/24上半财年业绩。财报显示&#xff0c;公司营收和净利润连续6年创同期历史新高&#xff0c;经营溢利增速再次快于收入增速。 自2018 年战略转型坚持“聚焦主航道、聚焦主品牌”后&#xf…