关于滚动数组的一些菜鸟随笔

什么是滚动数组

    简单来说,滚动数组就是一种具有短暂记忆力的数组,它会牺牲时间来节省空间,用size=3的数组来“存储”30000个数据。这么说有点离谱、抽象,毕竟a[3]怎么存储a[30000]里面的东西呢。这就是滚动数组的特性,即只记录少量的后续需要使用的数据,而将之前用过且不再需要调用的数据抛弃、覆盖,这样就将a[30000]中不要的数据所占的空间节省出来,以达到a[3]就能达成的任务目标。

滚动数组的核心:取余

    在开始学习C语音的时候,接触到了一个新的数学运算符:取余%,和除号 / 类似的是都多用在特殊的循环或者是取一串数字的某一位,除法多取高位,取余多取低位。在滚动数组中,取余用于数组下标的动态改变,以达到[3]存[30000]的效果,例如:

int m=30000;//一个原先大的数据空间
int n=3;//所需要的一个滚动数组空间
void fun()
{

    for (int i = 0; i < m; i++)
    {
        d[i % n] = d[(i - 1) % n] + d[(i - 2) % n];
    }
}

    通过取余的特点可以看出,动态数组在取模和循环的作用下只用3个空间就可以做到存储30000个数据的作用。

使用情况(浅提动态规划)

  那什么时候使用呢?

  1. 在不在意时间,只需要节省空间的情况。滚动数组的本质是通过for循环多次覆盖不用的数值,增加了时间,又使用取余动态改变数组下标,节省了空间。
  2. 多用于动态规划问题(Dynamic Programing,DP)。  

  在这不得不说到动态规划问题,但由于篇幅所限,在此仅浅谈一下,后续有缘更新。DP主要用于求解以时间划分阶段的动态过程的优化问题,但是一些与时间无关的静态规划(如线性规划、非线性规划),只要人为地引进时间因素,把它视为多阶段决策过程,也可以用动态规划方法方便地求解。

    多阶段决策过程的特点是每个阶段都要进行决策,具有n个阶段的决策过程的策略是由n个相继进行的阶段决策构成的决策序列。由于前阶段的终止状态又是后一阶段的初始状态,因此确定阶段最优决策不能只从本阶段的效应出发,必须通盘考虑,整体规划。就是说,阶段k的最优决策不应只是本阶段的最优,而必须是本阶段及其所有后续阶段的总体最优。

   而DP 的有最重要的两个理论--最优化原理和无后效性原则

例题说明

  斐波那契数列

  1. 因为乘数指定,即只有一条路能走,故符合最优原理。
  2. 乘积固定,没有其它因素影响,所以符合无后效性原则。

因此可以使用滚动数组方法,代码:

 1 void func2()
 2 {
 3     int d[3];
 4     d[0] = 1;
 5     d[1] = 1;
 6     for (int i = 0; i < 100; i++)
 7     {
 8         d[i % 3] = d[(i - 1) % 3] + d[(i - 2) % 3];
 9     }
10     printf("%d", d[99 % 3]);
11 }

  01背包

  1. 整体最优是由一步步的子问题最优组成,即n个空间的包最优解是由1~n-1个空间背包的最优解组合而成,故符合最优原理。
  2. 每个数值固定,无论前面问题是怎样解,后面背包总空间不变,往后的任何决策都不会改变。故符合无后效性。

因此可以使用滚动数组方法,代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 1e3+10;
 4 int t,n,v;
 5 int dp[maxn];
 6 int value[maxn];
 7 int weight[maxn];
 8 int main()
 9 {
10     scanf("%d",&t);
11     while(t--)
12     {
13         memset(dp,0,sizeof dp);
14         scanf("%d %d",&n,&v);
15         for(int i = 1;i <= n;i++)
16             scanf("%d",&value[i]);
17         for(int i = 1;i <= n;i++)
18             scanf("%d",&weight[i]);
19         // for(int i = 1;i <= n;i++)    原始二维dp方程
20         //     for(int j = 0;j <= v;j++)
21         //     {
22         //         if(j >= weight[i])        //若取得下,则可以选择取或不取
23         //             dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
24         //         else    
25         //             dp[i][j]=dp[i-1][j];
26         //     }
27         for(int i = 1;i <= n;i++)    //使用滚动数组优化后的dp方程
28             for(int j = v;j >= weight[i];j--)    //倒序保证数据更新的有序性,保证只取一次,正序则是完全背包的写法
29                 dp[j]=max(dp[j],dp[j - weight[i]] + value[i]);
30         printf("%d\n",dp[v]);
31     }
32     return 0;
33 }

<-------------------------------------------------------------------------------------------->

最后,滚动数组只是动态问题中的一小部分,后续还有更多有趣的知识,例如动态问题和搜索、分治法的联系和区别,随缘更新。By the way,更新这篇时正赶上国庆,摆了几天,当我正常学习的时候,电脑开摆了,这么点字,它疯狂蓝屏、黑屏,问题是没保存,导致写了好几次,有些小细节因为写太多烦了,就没打,现在反而忘了,华硕天选2,你恶事做尽!!!!

文章部分节选:

动态规划之滚动数组 - shawchakong - 博客园 (cnblogs.com)

滚动数组(简单说明)_儒rs的博客-CSDN博客_滚动数组

posted @ 2022-10-11 00:32  000100110111  阅读(102)  评论(0编辑  收藏  举报