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

背包问题的重中之重是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.mfbz.cn/a/199925.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[DASCTF 2023 0X401七月暑期挑战赛] web刷题记录

文章目录 EzFlask方法一 python原型链污染方法二 flask框架静态文件方法三 pin码计算 MyPicDisk方法一 字符串拼接执行命令方法二 phar反序列化 ez_cms EzFlask 考点&#xff1a;python原型链污染、flask框架理解、pin码计算 源码如下 import uuidfrom flask import Flask, re…

python与机器学习1,机器学习的一些基础知识概述(完善ing)

目录 1 AI ,ML,DL,NN 等等概念分类 1.1 人工智能、机器学习、深度学习、神经网络之间的关系&#xff1a; 1.2 人工智能的发展 2 ML机器学习的分类&#xff1a;SL, USL,RL 2.1 机器学习的分类 2.2 具体的应用举例 2.3 数据分类 3 关于阈值θ和偏移量b的由来 4 不同的激…

VMware虚机重启后静态IP不生效

配置好一套虚机之后&#xff0c;因为重启电脑&#xff0c;导致虚机的静态ip配置不生效&#xff0c;xshell连接不上虚机。以下是自查和解决方案&#xff1a; 1.使用su -进入root用户 2.查看打开虚机的teminal窗口查看配置的ip文件&#xff1a;vim /etc/sysconfig/network-script…

使用Accelerate库在多GPU上进行LLM推理

大型语言模型(llm)已经彻底改变了自然语言处理领域。随着这些模型在规模和复杂性上的增长&#xff0c;推理的计算需求也显著增加。为了应对这一挑战利用多个gpu变得至关重要。 所以本文将在多个gpu上并行执行推理&#xff0c;主要包括&#xff1a;Accelerate库介绍&#xff0c;…

【APUE】进程间通信

目录 一、管道 1.1 匿名管道 1.2 命名管道 二、XSI IPC 2.1 概述 2.2 消息队列 2.2.1 msgget 2.2.2 msgsnd 2.2.3 msgrcv 2.2.4 msgctl 2.2.5 代码示例 2.3 信号量数组 2.3.1 semget 2.3.2 semop 2.3.3 semctl 2.3.4 代码示例 2.3 共享内存 2.3.1 shmget…

PVE中CT容器安装openwrt X86的极简方法

下载推荐&#xff1a;https://openwrt.ai/ 使用环境PVE8.0&#xff0c;openwrt是以上网址的最新版&#xff0c;内涵及其丰富组件。 问题来源&#xff1a; 在PVE虚拟机可以很方便的使用img文件&#xff0c;转换qm 成一个硬盘文件&#xff0c;加入到虚拟机也就完成了&#xff0c…

《git小白笔记·一》记住这些Git使用就够了

到底选gitlab还是选github&#xff1f;&#xff1a; 只要掌握这些git命令完全够用&#xff1a; &#xff08;1&#xff09;首先我们先下载一个git&#xff0c;下载完成后属于右键会看到git GUI here和git Bash here&#xff0c;我们常用git Bash here&#xff0c;打开Git我们看…

台灯哪种灯光对眼睛好?精选高品质的护眼台灯

近几年越来越多家长开始重视孩子的视力健康问题&#xff0c;要知道如今我国儿童青少年的近视率已经达到了52.7%&#xff0c;也就是说平均每十个儿童青少年中就有五个是存在视力问题的&#xff0c;而且近视的人群是越往高年级的学生占比越多。不过目前也发现了不少低年级的孩子出…

嵌入式总线技术详解

1. 总线概述 1.1 总线定义 总线&#xff08;Bus&#xff09;是计算机各种功能部件之间传送信息的公共通信干线它是由导线组成的传输线束&#xff0c;按照计算机所传输的信息种类&#xff0c;计算机的总线可以划分为数据总线、地址总线和控制总线&#xff0c;分别用来传输数据…

Python开发——工具篇 Pycharm的相关配置,Python相关操作 持续更新

前言 本篇博客是python开发的工具篇相关&#xff0c;介绍pycharm的使用和相关配置&#xff0c;收录python的相关操作&#xff0c;比如如何启动jupyter。 目录 前言引出Pycharmpycharm如何不同等级日志显示不同颜色设置不同pycharm的python环境 Python操作如何启动Jupyter 总结…

电脑资料删除后如何恢复?3个简单方法轻松恢复文件!

“我平常喜欢在电脑上保存很多学习资料&#xff0c;但由于资料太多&#xff0c;在清理电脑时我可能会误删一些比较有用的资料。想问问大家电脑资料删除后还有机会恢复吗&#xff1f;应该怎么操作呢&#xff1f;” 在数字化时代&#xff0c;很多用户都会选择将重要的文件直接保存…

如何在安卓Termux中使用SFTP文件传输并结合内网穿透工具实现远程传输

文章目录 1. 安装openSSH2. 安装cpolar3. 远程SFTP连接配置4. 远程SFTP访问4. 配置固定远程连接地址 SFTP&#xff08;SSH File Transfer Protocol&#xff09;是一种基于SSH&#xff08;Secure Shell&#xff09;安全协议的文件传输协议。与FTP协议相比&#xff0c;SFTP使用了…

Java(九)(多线程,线程安全,实现线程的方法,线程同步,线程池,并发和并行,线程的六种状态)

目录 多线程 线程 实现线程的方法 方法一:继承Thread父类 方法二:实现Runnable接口 方法三:Callable接口和FutureTask类来实现 Thread方法 线程安全 线程同步 同步代码块 同步方法 Lock锁 线程池 线程池对象的创建方式一: 线程池处理Runnable任务 线程池处理Cal…

Linux概述

Linux概述 1、操作系统 ​ 定义&#xff1a;操作系统(Operating System&#xff0c;简称OS)是管理计算机硬件与软件资源的计算机程序 ​ 作用&#xff1a;是把计算机系统中对硬件设备的操作封装起来&#xff0c;供应用软件调用&#xff0c;也是提供一个让用户与系统交互的操…

数据安全建设的六大关键步骤

随着数字化时代的到来&#xff0c;数据安全已经成为企业和社会组织必须面对的重要问题。数据泄露、网络攻击等安全事件频发&#xff0c;给个人隐私、企业利益和国家安全带来了严重威胁。因此&#xff0c;加强数据安全建设已成为刻不容缓的任务。以下是数据安全建设的六大关键步…

“SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展

近年来&#xff0c;国内外学者在生态系统的敏感性、适应能力和潜在影响等方面开展了大量的生态脆弱性研究&#xff0c;他们普遍将生态脆弱性概念与农牧交错带、喀斯特地区、黄土高原区、流域、城市等相结合&#xff0c;评价不同类型研究区的生态脆弱特征&#xff0c;其研究内容…

Vue2 若依框架头像上传 全部代码

<template><div><div class"user-info-head" click"editCropper()"><img v-bind:src"options.img" title"点击上传头像"class"img-circle img-lg" /></div><el-dialog :title"title&…

没AI背景的高级产品经理,可能快做不下去了

‍ 一、现状 1个多月前&#xff0c;我在给某高P团员“1v1咨询”时提到&#xff0c;虽然目前各家AI 2.0公司里&#xff0c;高级坑位都被占了&#xff0c;但春节前后可能会空出来一些。 底层原因在于&#xff0c;今年有一些AI背景较少的高级产品经理&#xff0c;趁热跳到AI公司&a…

高防CDN可以起到什么作用?

高防CDN相对于普通的CDN加速&#xff0c;除了具备基础的加速功效外&#xff0c;高防CDN在每一节点上均有相应配置的防御功效&#xff0c;不仅具备了隐藏源站不被攻击的优势&#xff0c;也具备了访问加速&#xff0c;多节点防御的功效。随着互联网的不断发展&#xff0c;网络上的…

error “you should set MAGICKCORE_HDRI_ENABLE

最近做一个项目需要配置ImageMagick库&#xff0c;本项目配置环境如下&#xff1a; ImageMagick version 7 Operating system, version and so on ubuntu 20.04 Description error "you should set MAGICKCORE_HDRI_ENABLE 查阅网上的资料&#xff1a; 默认的是DMAGICKC…
最新文章