【滚动数组简介】
● 滚动数组是一种动态规划中的空间优化技术,通过只维护 DP 状态的必要部分来减少内存使用,特别适用于状态转移仅依赖于前一层或少数几个状态的场景。
● 滚动数组的核心思想是利用状态转移的局部依赖性
例如,在 0-1 背包问题中,二维 DP 数组 dp[i][j] 表示前 i 个物品在容量 j 下的最大价值,其转移方程 dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]) 表明当前行只依赖上一行。因此,可以用两个一维数组交替更新,或进一步优化为单个一维数组 dp[j],其中 dp[j] 直接代表当前容量下的最优值,从而将空间复杂度从 O(n×W) 降至 O(W)。
● 滚动数组的关键实现细节包括遍历顺序和初始化
遍历顺序需避免状态覆盖。例如,在 0-1 背包中,背包容量必须从大到小遍历(倒序),以确保每个物品只被考虑一次;初始化则需根据问题定义进行,例如背包问题中 dp 初始化为 0,其余为 0 或负无穷。
● 滚动数组与状态压缩的区别在于优化维度
++滚动数组侧重于减少维度++ (如二维转一维),而++状态压缩则通过位运算等将状态编码为整数++ (如用二进制表示集合),适用于状态空间较小的场景(如旅行商问题)。++滚动数组与状态压缩两者可结合使用++ ,但滚动数组更通用且易于实现。
【滚动数组应用】
下面以斐波那契数列问题为例,来简单感受一下滚动数组的魅力。
下面代码是斐波那契数列的经典实现,需要 n 个存储位置,即空间复杂度为 O(n)。
cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn=100;
int a[maxn];
int main() {
int n;
cin>>n;
for(int i=1; i<=n; i++) {
if(i==1 || i==2) a[i]=1;
else a[i]=a[i-1]+a[i-2];
cout<<a[i]<<" ";
}
return 0;
}
/*
in:10
out:1 1 2 3 5 8 13 21 34 55
*/
斐波那契数列的计算可以通过滚动数组技术实现空间优化。
具体而言,在迭代过程中只需维护三个变量:两个用于存储前两个斐波那契数,一个用于计算当前值。每次迭代时,用新计算的值覆盖最旧的数据,从而将空间复杂度降至 O(1)。
下面代码是滚动数组优化的斐波那契数列求解,仅需三个存储位置,空间复杂度为 O(1)。
cpp
#include<bits/stdc++.h>
using namespace std;
int a[3];
int main() {
int n;
cin>>n;
a[0]=1,a[1]=1;
for(int i=0; i<n; i++) {
if(i==0 || i==1) cout<<1<<" ";
else {
a[2]=a[0]+a[1];
a[0]=a[1];
a[1]=a[2];
cout<<a[2]<<" ";
}
}
return 0;
}
/*
in:10
out:1 1 2 3 5 8 13 21 34 55
*/
下面代码是滚动数组优化的斐波那契数列求解的另一种写法。
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
int n;
cin>>n;
int pre=0,cur=1,nxt=0;
for(int i=1; i<=n; i++) {
if(i==1) cout<<cur<<" ";
else {
nxt=pre+cur;
pre=cur;
cur=nxt;
cout<<cur<<" ";
}
}
return 0;
}
/*
in:10
out:1 1 2 3 5 8 13 21 34 55
*/
【参考文献】
https://blog.csdn.net/weixin_47750287/article/details/114021450
https://www.cnblogs.com/CodeWater404/p/16140295.html