前提概要

首先呢,滚动数组是一种能够在动态规划中降低空间复杂度的方法,
有时某些二维dp方程可以直接降阶到一维,在某些题目中甚至可以降低时间复杂度,是一种极为巧妙的思想,
简要来说就是通过观察dp方程来判断需要使用哪些数据,可以抛弃哪些数据,
一旦找到关系,就可以用新的数据不断覆盖旧的数据量来减少空间的使用,
接下来我会介绍一些简单例题来具体解释一下滚动数组的作用。

先以斐波那契数列为例
我们先以斐波那契数列来简单感受一下滚动数组的魅力,先上一段经典的代码(使用滚动数组)

#include<bits/stdc++.h>
using namespace std;
int main()
{int a[37];a[0]=1;a[1]=1;//求斐波那契数列第37个数for(int i=2;i<=36;i++){a[i]=a[i-1]+a[i-2];}printf("%d\n",a[36]);return 0;}
#include<bits/stdc++.h>
using namespace std;
int main()
{int a[3];a[0] = 1;a[1] = 1;for(int i = 1;i <= 35;i++){a[2] = a[0] + a[1];a[0] = a[1];a[1] = a[2];}printf("%d\n",a[2]); return 0;
}

通过观察斐波那契数列方程 f(n)= f(n-1)+ f(n-2),我们可以发现,
我们实际上只需要前两个递推的数求和即可,于是我们可以使用数组的前三个位置来分别存贮数据, 待计算完之后,再用新的数据将旧数据覆盖。
这样我们 本来需要用三十多个位置的数组,最终却只用了三个位置, 大大减少了空间复杂度。
对于某些只需要最终答案的题目,我们可以抛弃掉当中一些不必要存贮的数据,来减少空间的使用。

再以0/1背包为例

这里我们以HDU2602的bone collector题目为例子,
对于每一个状态,我们都可以判断这次是取还是不取或是取不下。
枚举第i个物品,枚举j代表背包可以放的下的体积。
若不取或者取不下,那么结果就是dp[i-1][j],
若取,就由前面的状态预留出weight[i]的位置再加上i物品的价值,即dp[i-1][j-weight[i]]+value[i]。

可得二维dp方程dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])
我们先来看二维的dp矩阵,
背包价值是{1,2,3,4,5},对应的体积是{5,4,3,2,1};

以dp[4][j]->dp[5][j]为例,此时第5个物品的体积为1,价值为5

我们可以清楚的看出来,dp方程递归过程是不断地由上一行的数据传递到下一行,
比如最后dp[5][10]是由dp[5][10]=max(dp[4,10],dp[4,9]+5))=14推得的,
也就是说当我们递推到dp[i][j]时,对于那些只要求最终最佳答案的题目来说,只需要i-1这行的数据即可,至于上面的i-2,i-3都是不需要的数据,
题目如果并没有要求中间的状态(比如输出背包的方案),我们就可以将其省略来节省空间的使用。
所以我们可以只用一维数组dp[j]来记录数据dp[i][j]的状态,在更新的过程中不断用新的数据dp[j] (dp[i][j]) 覆盖掉旧的数据dp[j](dp[i-1][j])。

为什么j维度在01背包是逆序,完全背包是正序呢

  • 我相信会有很多同学对01背包第二维j为什么是倒着递推有疑惑。
    我们从正序进行,假设当i=5,j=9时,那么此时dp[9]是由前面的dp[8]和dp[9]递推更新,那么现在dp[9]实际上存贮的是递推得到的dp[5][9]而并非旧数据dp[4][9],那么也就不能保证dp[5][10]的递推成功(dp[5][10]正确值应该是由dp[4][10]和dp[4][9]递推得到的) ,但正序的做法却意味着可以多次调用前面的数据,相当于多次取用物品,也就是完全背包(物品可取次数为无限次)的思路了。
  • 那该怎么确保不覆盖dp[9]存贮的数据dp[4][9]呢,那么倒序就起作用了。假设当i=5,j=10时,dp[10]内的数据存贮的是dp[4][10]的数据,由于j从尾部开始枚举,dp[10]就会由dp[9]和dp[10]递推得到(dp[9]存贮的是dp[4][9],dp[10]存贮的是dp[4][10]),那么此时dp[10]的值就更新为了dp[5][10]。然后依次类推,因为是倒序的缘故,那么接下来枚举的j都要比先前的j要来的小,所以当某个位置更新数据时,并不会调用其位置后面的数据,一定会是先调用其位置之前的旧数据,然后再将当前位置更新覆盖掉原来的旧数据,也就保证了数据更新的有序性。

小结

对于动态规划题目来说,我们可以先写出最原始的dp方程,再通过观察dp方程,使用滚动数组进行优化,我们需要思考如何更新数据和覆盖数据来达到降维的目的(可能需要很长的时间思考,不过熟能生巧)。

具体代码解析

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int t,n,v;
int dp[maxn];
int value[maxn];
int weight[maxn];
int main()
{scanf("%d",&t);while(t--){memset(dp,0,sizeof dp);scanf("%d %d",&n,&v);for(int i = 1;i <= n;i++)scanf("%d",&value[i]);for(int i = 1;i <= n;i++)scanf("%d",&weight[i]);// for(int i = 1;i <= n;i++) 原始二维dp方程//     for(int j = 0;j <= v;j++)//     {//         if(j >= weight[i])        //若取得下,则可以选择取或不取//             dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);//         else  //             dp[i][j]=dp[i-1][j];//     }for(int i = 1;i <= n;i++)    //使用滚动数组优化后的dp方程for(int j = v;j >= weight[i];j--)  //倒序保证数据更新的有序性,保证只取一次,正序则是完全背包的写法dp[j]=max(dp[j],dp[j - weight[i]] + value[i]);printf("%d\n",dp[v]);}return 0;
}

滚动数组(简单说明)相关推荐

  1. 关于滚动数组的一些初学随笔

    什么是滚动数组 简单来说,滚动数组就是一种具有短暂记忆力的数组,它会牺牲时间来节省空间,用size=3的数组来"存储"30000个数据.这么说有点离谱.抽象,毕竟a[3]怎么存储a ...

  2. 高级打字机【主席树】【滚动数组】【块状链表】

    题目大意: 一个计算机支持一下三中操作: TT xx:在文章末尾打下一个小写字母xx. UU xx:撤销最后的xx次修改操作. QQ xx:询问当前文章中第xx个字母并输出. InputInput 7 ...

  3. 《滚动数组》---滚动数组思想,运用在动态规划当中

    滚动数组是DP中的一种编程思想.简单的理解就是让数组滚动起来,每次都使用固定的几个存储空间,来达到压缩,节省存储空间的作用.起到优化空间,主要应用在递推或动态规划中(如01背包问题).因为DP题目是一 ...

  4. 滚动数组~\(≧▽≦)/~

    今天第一次用了滚动数组,缘由要从一道题说起:POJ 1159 Palindrome 题意:给你一个字符串,求对字符串最少添加几个字符可变为回文串. 分析: 简单做法是直接对它和它的逆序串求最长公共子序 ...

  5. Gym 100507G The Debut Album (滚动数组dp)

    The Debut Album 题目链接: http://acm.hust.edu.cn/vjudge/contest/126546#problem/G Description Pop-group & ...

  6. dp笔记:关于DP算法和滚动数组优化的思考

    从网上总结了一些dp的套路以及对滚动数组的一些思考,现记录如下,希望以后回顾此类算法时会有所帮助. 目录 1.DP算法经验 1.DP算法核心: 2.DP算法类别以及例题 例1:三步问题 例2:最小路径 ...

  7. 递推算法之滚动数组思维方式

    概述 在算法的最终结果只用到本层与上一层的结果时, 可以使用滚动数组思想. 简单的理解就是每次都使用固定的几个存储空间达到压缩节省存储空间的作用, 主要用在递推算法中. 示例1: 爬楼梯问题 假设你正 ...

  8. 终于结束的起点(滚动数组,记忆化搜索)

    任意门 终于结束的起点 题目背景 终于结束的起点 终于写下句点 终于我们告别 终于我们又回到原点 -- 一个个 OIer 的竞赛生涯总是从一场 NOIp 开始,大多也在一场 NOIp 中结束,好似一次 ...

  9. 算法基础:动态规划数组中滚动数组的使用

    这篇文章继续在前一篇文章的基础上介绍动态规划数组的优化方式.很多基础算法本来都是写给我家的小少年看的,结果发现后浪学习的速度远远超出我的想象,在一个周末用这篇文章来纪念一下吧. 目录 斐波那契数列 d ...

最新文章

  1. unity中使用自定义shader进行光照贴图烘培无法出现透明度的坑爹问题
  2. python输入一个正整数n求下列算式的值_C语言编写程序:输入一个正整数x和一个正整数n,求下列算式的值。,C语言 编写一个程序,输入一个正整数,求出它是几位数。...
  3. 山西五台警方通报“男子强拽女学生”:嫌疑人被刑拘
  4. python学习笔记四——数据类型
  5. 转:VMware、微软等四种主要的网络IO虚拟化模型
  6. ubuntu系统设置开机自启动
  7. Android源码解析:UI绘制流程之测量.md
  8. 非编程人学Python,要注意哪些隐秘的错误认知?
  9. tar+openssl加密压缩解压缩
  10. 应急管理大数据ppt_大数据应急管理.doc
  11. java setproperty 未生效_Java System类setProperty()方法及示例
  12. 汇总我关注的技术博主的2021年度总结
  13. 金彩教育:拼多多运营的方法有哪些
  14. 程序设计的感悟——大道至简
  15. java list 内存释放_QList内存释放(看它内部存储的是否是Object,另外还有qDeleteAll)...
  16. RocketMQ 优雅停机技巧
  17. 多人配音怎么做的?这两个多人配音方法分享给你
  18. pwm一个时间单位_「硬见小百科」什么是PWM“死区”?
  19. 软件测试app访问相机,PlayMemories Mobile应用实测,让相机成为你手机的外置摄像头...
  20. Web Atoms Crack,JavaScript 桥接器

热门文章

  1. word从当前页面开始页码
  2. 计算机无法识别苹果6手机,电脑无法识别iphone 连接手机后手机为弹… - Apple 社区...
  3. 使用 Prometheus 实现邮件/企业微信告警
  4. BetterZip——技巧:添加经常使用的文件夹!
  5. 新闻采访如何运用计算机技术学校,新闻采访教学方法和改革.doc
  6. 年营收相差一半,华为凭什么和苹果比?
  7. 从员工到总经理的成长笔记:自慢(2)
  8. 网页制作 手机端与PC端兼容
  9. 回收站删除了文件怎么恢复?
  10. h5微信f分享链接给对方获取对方手机号_如何在30秒完成【H5】专属贺卡制作?...