利用滚动数组优化

数组是最常用的数据结构之一,现在我们对数组的下标进行特殊处理,使每一次操作仅保留若干有用信息,新的元素不断循环刷新,看上去数组的空间被滚动地利用,此模型我们称其为滚动数组。其主要达到压缩存储的作用,一般常用在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(nm)下降到O(2m)。在存储过程中,我们设定一个变量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],实现了数组的滚动效果。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值