acwing 动态规划dp 0 1背包问题

                                                               前言

         hello小伙伴们,最近由于个人放假原因颓废了一段时间很长时间没有更新CSDN的内容了,唉,毕竟懂得都懂寒暑假静下心来学习的难度远比在学校里大的多。

        但是,也不是毫无办法克服,今天我来了我们当地的一家自习室来学习,感觉效果比在家强很多,趁机写一下博客分享一下最近的收获。

        今天没写蓝桥杯备赛系列因为我感觉这块内容应该是蓝桥杯的一个重点考察方向,所以我想先讲知识点然后过渡讲蓝桥杯系列,包括dfs、bfs那块内容也是这个套路,尽量是能让我和大家收获最大为好。不多bb上内容。

                                                                DP 01背包模型

DP(动态规划) 内容核心讲解

 

状态表示:用一个数组f[][](数组可能是一维也可能是二维,根据具体题目具体分析)来表示某个集合,这个集合表示所有的做法,集合存的值就是对应做法的属性(一般是max,min,count)(换句话说:f[i][j]表示在限制i,j下做法的属性)
状态转移:本质上是一个优化的过程,就是不断更新状态。

01背包问题

思路

重要变量说明:

f[][]:用于状态表示;

w[]:记录每个物品的价值

v[]:记录每个物品的体积

1. 定义二维数组f[][],其中f[i][j]表示在前i个物品,背包容积为j的限制下所能装下的最大价值。这里的f[i][j]就是做法的集合,f[i][j]的值就是最大价值即属性。
2. 从i=1开始枚举,对于第i个物品,都有选和不选两种选择:
如果不选第i个物品,那么状态转移方程为f[i][j]=f[i-1][j]
如果选择第i个物品,那么状态转移方程为f[i][j]=f[i-1][j-v[i]]+w[i]
3. 我们因为要求最大价值,所以对上面两种情况去max即可

为什么可以转为一维

首先观察状态转移方程 dp[i][j]是由 dp[i-1][jxxxx]推导而来,仅看第一个维度,即i - 1 与 i ,可以发现第i层是由上一层推导而来的。

故我们不必要保存i - 2 层,比如我们计算第三层是只需要第二层的。不需要第一层的数据。

当我们去掉i时,即我们不需要控制第几层,只需要长度为j的数组,保存确认过最新的一层。作为下一层的参考。例如我们计算第三层dp时,此时dp原数据保存的是第二层的结果。

为什么要逆序

首先,通过上一个问题,我们确认了我们目前一维的dp数组,保存的是确认过的最新一层的数据,即上一层的数据。

当我们计算当前层时,对于二维时的状态转移方程有

dp[i][j] = max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);

可以看到,dp[i - 1][j - v[i]] + w[i] 使用的上一层的原始数据(dp[i - 1]),而我们使用一维的状态转移方程时有

dp[j] = max(dp[j], dp[j - v[i]] + w[i]);

当我们从小到大更新是, 因为j - v[i] 是严格小于j 的,所以我们可以举个例子 dp[3] = max(dp[3], dp[2] + 1); 因为我们是从小到大更新的,所以当更新到dp[3]的时候,dp[2]已经更新过了,已经不是上一层的dp[2]。

而当我们逆序更新时有,举例 dp[8] = max(dp[8], dp[6] + 2)当更新dp[8]时,dp[6]还没有被更新,还是上一层的数据,这样才能保证没有读入脏数据。

#include<iostream>

using namespace std;
const int N=1010;
int f[N][N],v[N],w[N];

int main()
{
    int n,V;
    cin>>n>>V;
    for(int i=1;i<=n;i++)   
        cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)       //从第一个物品开始选,直到最后一个物品结束
        for(int j=1;j<=V;j++)       //从最小的体积开始,直到背包的最大的容积
        {
            if(j-v[i]>=0)       //可以装第i个物品
                f[i][j]=f[i-1][j-v[i]]+w[i];        //状态转移
            f[i][j]=max(f[i][j],f[i-1][j]);     //状态转移,两种情况取最大值
        }
    cout<<f[n][V]<<endl;
    return 0;
}

 

优化1(滚动数组)

注意: 这里优化的的是空间而不是时间

思路

我们注意到其实上面写的f[i][j]其实每次计算只是用到了第i层和第i-1层,所以我们数组的第一维其实只用开两个即可。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int v[N];
int w[N];
int f[N][N];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i ++) scanf("%d%d",&v[i],&w[i]);
    for(int i = 1;i <= n;i ++){
        for(int j = 0;j <= m;j ++){
            f[i % 2][j] = f[(i - 1) % 2][j];
            if(j >= v[i]) f[i % 2][j] = max(f[i % 2][j],f[(i - 1) % 2][j - v[i]] + w[i]);
        }
    }
    printf("%d",f[n % 2][m]);
    return 0;
}

 

优化2(一维数组)


我们可以把二维数组优化到一维数组
为什么可以这样变形呢?我们定义的状态f[i][j]可以求得任意合法的i与j最优解,但题目只需要求得最终状态f[n][m],因此我们只需要一维的空间来更新状态

思路


定义f[]表示状态,f[j]表示在N个物品,背包容积为j下所能装下的最大价值。
也还是从i开始枚举,表示选与不选第i个物品。
注意
我们要逆序枚举背包容积,即每次循环从背包的最大容积开始枚举。
**原因如下:**在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用第i-1轮的状态却用的是第i轮的状态。
**简单来说:**简单来说,一维情况正序更新状态f[j]需要用到前面计算的状态已经被「污染」,逆序则不会有这样的问题。

来个例子

    如果 j 层循环是递增的: 
    for (int i = 1; i <= n; i++) {
        for (int j = v[i]; j <= m; j++) {
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
 

 经典代数法模拟运行过程
    当还未进入循环时:
    f[0] = 0;  f[1] = 0;  f[2] = 0;  f[3] = 0;  f[4] = 0;  
    f[5] = 0;  f[6] = 0;  f[7] = 0;  f[8] = 0;  f[9] = 0; f[10] = 0;
    当进入循环 i == 1 时:
    f[4] = max(f[4], f[0] + 5); 即max(0, 5) = 5; 即f[4] = 5;
    f[5] = max(f[5], f[1] + 5); 即max(0, 5) = 5; 即f[5] = 5;
    f[6] = max(f[6], f[2] + 5); 即max(0, 5) = 5; 即f[6] = 5;
    f[7] = max(f[7], f[3] + 5); 即max(0, 5) = 5; 即f[7] = 5;
    重点来了!!!
    f[8] = max(f[8], f[4] + 5); 即max(0, 5 + 5) = 10; 即f[8] = 10;
    这里就已经出错了
    因为此时处于 i == 1 这一层,即物品只有一件,不存在单件物品满足价值为10
    所以已经出错了。

    如果 j 层循环是逆序的:
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >= v[i]; j--) {
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
 

代数法模拟过程
    当还未进入循环时:
    f[0] = 0;  f[1] = 0;  f[2] = 0;  f[3] = 0;  f[4] = 0;  
    f[5] = 0;  f[6] = 0;  f[7] = 0;  f[8] = 0;  f[9] = 0; f[10] = 0;
    当进入循环 i == 1 时:w[i] = 5; v[i] = 4;
    j = 10:f[10] = max(f[10], f[6] + 5); 即max(0, 5) = 5; 即f[10] = 5;
    j = 9 :f[9] = max(f[9], f[5] + 5); 即max(0, 5) = 5; 即f[9] = 5;
    j = 8 :f[8] = max(f[8], f[4] + 5); 即max(0, 5) = 5; 即f[8] = 5;
    j = 7 :f[7] = max(f[7], f[3] + 5); 即max(0, 5) = 5; 即f[7] = 5;
    j = 6 :f[6] = max(f[6], f[2] + 5); 即max(0, 5) = 5; 即f[6] = 5;
    j = 5 :f[5] = max(f[5], f[1] + 5); 即max(0, 5) = 5; 即f[5] = 5;
    j = 4 :f[6] = max(f[4], f[0] + 5); 即max(0, 5) = 5; 即f[4] = 5;
    当进入循环 i == 2 时:w[i] = 6; v[i] = 5; 
    j = 10:f[10] = max(f[10], f[5] + 6); 即max(5, 11) = 11; 即f[10] = 11;
    j = 9 :f[9] = max(f[9], f[4] + 6); 即max(5, 11) = 5; 即f[9] = 11;
    j = 8 :f[8] = max(f[8], f[3] + 6); 即max(5, 6) = 6; 即f[8] = 6;
    j = 7 :f[7] = max(f[7], f[2] + 6); 即max(5, 6) = 6; 即f[7] = 6;
    j = 6 :f[6] = max(f[6], f[1] + 6); 即max(5, 6) = 6; 即f[6] = 6;
    j = 5 :f[5] = max(f[5], f[0] + 6); 即max(5, 6) = 6; 即f[5] = 6;
    当进入循环 i == 3 时: w[i] = 7; v[i] = 6; 
    j = 10:f[10] = max(f[10], f[4] + 7); 即max(11, 12) = 12; 即f[10] = 12;
    j = 9 :f[9] = max(f[9], f[3] + 6); 即max(11, 6) = 11; 即f[9] = 11;
    j = 8 :f[8] = max(f[8], f[2] + 6); 即max(6, 6) = 6; 即f[8] = 6;
    j = 7 :f[7] = max(f[7], f[1] + 6); 即max(6, 6) = 6; 即f[7] = 6;
    j = 6 :f[6] = max(f[6], f[0] + 6); 即max(6, 6) = 6; 即f[6] = 6;
一维代码优化后 
#include<iostream>

using namespace std;
const int N=1010;
int f[N],v[N],w[N];

int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=m;j>=v[i];j--)        //逆序,防止被数据污染
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m]<<endl;
    return 0;
}

今天就讲完了全部内容,但是我从如下几个方面总结一下今天讲的内容

首先讲了dp问题的大思路,然后给大家放了一下acwing上面的那个例题,然后给大家讲解了一下为什么二维可以转一维。

下次讲全部背包问题,讲完dp问题上蓝桥杯习题课系列,到时候就不讲知识点了。

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

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

相关文章

大数据开发之Spark(RDD弹性分布式数据集)

第 1 章&#xff1a;rdd概述 1.1 什么是rdd rdd&#xff08;resilient distributed dataset&#xff09;叫做弹性分布式数据集&#xff0c;是spark中最基本的数据抽象。 代码中是一个抽象类&#xff0c;它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合。 1.1…

【操作工具】IDEA的properties文件变为灰色的解决办法

背景 赋值了一份properties文件放到项目下面&#xff0c;但是里面的key都是灰色的 解决方案 去掉下面3后面对应的勾 去掉之后

Java零基础学习18:字符串

编写博客目的&#xff1a;本系列博客均根据B站黑马程序员系列视频学习和编写目的在于记录自己的学习点滴&#xff0c;方便后续回忆和查找相关知识点&#xff0c;不足之处恳请各位有缘的朋友指正。 一、字符串拼接 第一题&#xff1a;false 第二题&#xff1a;true 二、 字符串…

Java项目:12 Springboot的垃圾回收管理系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 1.介绍 垃圾分类查询管理系统&#xff0c;对不懂的垃圾进行查询进行分类并可以预约上门回收垃圾。 让用户自己分类垃圾&#xff0c; 按国家标准自己分类&#x…

LabVIEW高级CAN通信系统

LabVIEW高级CAN通信系统 在现代卫星通信和数据处理领域&#xff0c;精确的数据管理和控制系统是至关重要的。设计了一个基于LabVIEW的CAN通信系统&#xff0c;它结合了FPGA技术和LabVIEW软件&#xff0c;主要应用于模拟卫星平台的数据交换。这个系统的设计不仅充分体现了FPGA在…

CSS实现文本和图片无限滚动动画

Demo图如下&#xff1a; <style>* {margin: 0;padding: 0;box-sizing: border-box;font-family: Poppins, sans-serif;}body {min-height: 100vh;background-color: rgb(11, 11, 11);color: #fff;display: flex;flex-direction: column;justify-content: center;align-i…

2024 年值得收藏的 6 大 iPad 恢复软件

众所周知&#xff0c;数据丢失是 iOS 用户的普遍问题。由于意外删除、软件更新、被盗等多种原因&#xff0c;您可能会丢失重要文件。通过备份&#xff0c;您可以轻松找回 iPad上丢失的文件。但是&#xff0c;当您没有可用的备份时&#xff0c;麻烦就开始了。那么&#xff0c;如…

如何高效挖掘Web漏洞?

简介 SRC漏洞平台&#xff1a;安全应急响应中心&#xff08;SRC, Security Response Center&#xff09;&#xff0c;是企业用于对外接收来自用户发现并报告的产品安全漏洞的站点。说白了&#xff0c;就是连接白帽子和企业的平台&#xff0c;你去合法提交漏洞给他们&#xff0…

数据结构之树和森林

数据结构之树和森林 1、树的存储结构2、树和森林的遍历2.1、树的遍历2.2、森林的遍历 3、树、森林和二叉树之间的相互转换 数据结构是程序设计的重要基础&#xff0c;它所讨论的内容和技术对从事软件项目的开发有重要作用。学习数据结构要达到的目标是学会从问题出发&#xff0…

一键拥有你的GPT4

这几天我一直在帮朋友升级ChatGPT&#xff0c;现在已经可以闭眼操作了哈哈&#x1f61d;。我原本以为大家都已经用上GPT4&#xff0c;享受着它带来的巨大帮助时&#xff0c;但结果还挺让我吃惊的&#xff0c;还是有很多人仍苦于如何进行升级。所以就想着写篇教程来教会大家如何…

记录xxl-job重复执行引发业务问题

业务问题描述 1.创建运单&#xff0c;发现重复&#xff08;同一个车架号两条记录&#xff09; 2.通知重复反馈&#xff0c;A系统读取中间表状态为未处理数据&#xff0c;推送到B系统 原因分析 1.以上两个问题都是xxljob定时执行的 2.通过日志分析&#xff0c;读取中间表数…

pcl之滤波器(一)

pcl滤波器 pcl一共是有十二个主要模块&#xff0c;详细了解可以查看官网。https://pcl.readthedocs.io/projects/tutorials/en/latest/#basic-usage 今天学习一下pcl的滤波器模块。 滤波器模块&#xff0c;官网一共是提供了6个例程&#xff0c;今天先来看第一第二个。 直通…

01 Aras Innovator二次开发说明

在进行Aras Innovator二次开发之前&#xff0c;需要先了解Aras的服务器架构以及相关的方法论。 了解这部分内容后&#xff0c;有助于我们进行二次开发。 一. 服务器架构 参考下表&#xff1a; Aras Innovator为B/S架构&#xff0c;支持主流的浏览器(IE Edge,Firefox,Google)…

Labview for循环精讲

本文详细介绍Labview中For循环的使用方法&#xff0c;从所有细节让你透彻的看明白For循环是如何使用的&#xff0c;如果有帮助的话记得点赞加关注~ 1. For循环结构 从最简单的地方讲起&#xff0c;一个常用的for循环结构是由for循环结构框图、循环次数、循环计数(i)三部分组成…

Linux版本下载Centos操作

目录 一、Centos7 二、下载Centos7镜像 三、下载Centos7 买了个硬件安装裸机&#xff08;一堆硬件&#xff09; 把安装盘放到虚拟机里面&#xff0c;给机器加电 配置设置 ​编辑 网络配置 开启网络功能 四、安装linux客户端 Xshell是什么 Xshell使用&#xff08;连接…

构建一个安全可靠的身份认证中心和资源服务中心:SpringSecurity+OAuth2.0的完美结合

目录 1、引言 1.1 身份认证和授权的重要性 1.2 SpringSecurity和OAuth2.0的概述 2、架构设计 2.1 组件概述 2.2 身份认证中心的设计 2.3 资源服务中心的设计 3、身份认证中心的实现 3.1 用户管理 3.2 登录认证流程 3.3 令牌生成和管理 4、资源服务中心的实现 4.1 …

新版idea创建spring boot项目

目录 前言 汉化教程 项目模板初始化 1.点击新建项目 2.配置初始化信息 3.初始依赖选择 配置Maven 1.打开maven设置 2.重写maven配置文件 3.选择你创建的配置文件 4.重启项目 spring boot配置并测试 1.修改配置文件后缀 2.启动项目 3.编写测试控制类 4.重启项目…

idea引入jar包作为maven

1.引入jar包至项目中 2.配置当前项目的maven&#xff08;如果只想在本机能运行的话&#xff0c;到这一步就够了&#xff0c;后面pom配置也不需要这一步&#xff09; 3.配置文件的maven依赖路径 这里的 groupId 就是你引入原包的包路径&#xff0c;artifactId、version都是随…

【动态规划】【字符串】【C++算法】940. 不同的子序列 II

作者推荐 【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径 本文涉及知识点 动态规划汇总 LeetCode940. 不同的子序列 II 给定一个字符串 s&#xff0c;计算 s 的 不同非空子序列 的个数。因为结果可能很大&#xff0c;所以返回答案需要对 10^9 7 取…

企业如何用copilot?电通×Copilot:打破创意工作效率“天花板”

企业申请Azure OpenAI绿色通道 →记得评论私信~还可加入试用交流群~ 电通集团拥有着120年的历史、汇聚了七万多名精英&#xff0c;是全球顶级的创意公司之一。随着新兴传播渠道的不断涌现&#xff0c;电通的客户们面临着内容需求的挑战。好消息是&#xff0c;微软Copilot为电通…