滚动数组优化

数组是最常用的数据结构之一,现在我们对数组的下标进行特殊处理,使每一次操作仅保留若干有用信息,新的元素不断循环刷新,看上去数组的空间被滚动地利用,此模型我们称其为滚动数组。其主要达到压缩存储的作用,一般常用在DP类题目中。因为DP题目是一个自下而上的扩展过程,我们常常用到是连续的解,而每次用到的只是解集中的最后几个解,所以以滚动数组形式能大大减少内存开支。

一、二维滚动数组

例1:0-1背包问题

问题描述:

有 n 件物品x1, x2, …, xn , 每件物品有一个价值和一个重量,分别记为:

       v1,v2, …vn

       w1,w2, …wn

其中所有的 wi 均为整数。 现有一个背包,其最大载重量为m,要求从这n件物品中任取若干件(这些物品要么被装入要么被留下)。问背包中装入哪些物品可使得所装物品的价值和最大?

我们很容易得出状态转移方程:f(I,j) = max{f(I-1, j-w[I]) + v[I], f(I-1, j)}

例如,容量为10,有5个物体,

重量为3   5  1  9  7

价值为11 28  6  49 35

第十三讲 动态规划之滚动数组 - sxyckjzh - 随风飞扬

从推导过程中可以看出,第I阶段的状态值只与I-1阶段有关,以前的数据保存在那里已经毫无意义。为此,我们就想到了利用滚动数组进行优化。

具体实现时,我们可以将f数组的空间由[0…n,0..m]改为[0..1,0..m],空间复杂度由O(n*m)下降到O(2*m)。在存储过程中,我们设定一个变量c,则语句段为:

c:=0;

  for i:=1 to n do begin

    c:=1-c;

    for j:=1 to m do begin

      f[c,j]:=f[c,j-1];

      if j-t[i]>0 then

        if f[1-c,j-t[i]]+p[i]>f[c,j] then f[c,j]:=f[1-c,j-t[i]]+p[i];

    end;

  end;

这样,二维数组f的第一个下标值的取值就在0,1之间循环变化,实现了数组的滚动存储。

 

二、一维滚动数组

其实,在二维滚动数组的基础上,我们还可以优化为一维滚动数组,但此时滚动的方向尤其重要。例如上例的01背包我们可以降为一维,但在递推f[j]时应按m到0的顺序,这样才能保证推f[j]时f[j-w[i]]保存的是状态f[i-1,j-w[i]]的值。如上表中的第3阶段的数据如下表,在递推第4阶段时,最后状态f[10]的值是由f[10]的值和f[1]+49比较而来,此时,要保证f[1]的值是上一阶段的结果,若方向向反,就很难保证此值不被更新。

第十三讲 动态规划之滚动数组 - sxyckjzh - 随风飞扬

相应语句段为:

for i:=1 to n do

 for j:=m downto 0  do

          if (j>=wi) and (f[j-wi]+pi>f[j]) then

f[j]:=f[j-wi]+pi;

这样,新产生的数据将不断覆盖旧数据,实现了一维数据的滚动效果。

 

三、MOD滚动数组

滚动数组应用的条件是基于递推或递归的状态转移中,反复调用当前状态前的几个阶段的若干个状态,而每一次状态转移后,都有固定个数的状态失去作用。

滚动数组便是充分利用了那些失去作用的状态的空间,填补新的状态。

Mod 滚动数组主要应用在需要调用多个前面的阶段的状态的情况。

例2:楼梯有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,编一程序计算共有多少种不同的走法。

经过分析,此题的状态转移方程为:f[I]=f[I-1]+f[I-2],f[1]=1,f[2]=2

此时的f[I]只跟f[I-1]和f[I-2]有关,所以用滚动数组可优化空间,但如何实现调用两个前面的阶段的状态情况?就要用到MOD滚动数组。语句段为:

f[1]:=1;

f[2]:=2;

for I:=3 to n do

  f[I mod 3]:=f[(I-1) mod 3]+f[(I-2) mod 3];

这样,在程序执行过程中只利用了三个空间f[0],f[1]和f[2],实现了数组的滚动效果。

滚动数组实际是一种节约空间的办法,可根据具体题目要求选择相应的滚动数组进行优化。

posted @ 2017-09-13 17:16  Aragaki  阅读(1307)  评论(1编辑  收藏  举报