滚动数组
本文主要介绍一个十分实用的小技巧——滚动数组。它常被用来完成常数优化和减少代码量,且滚动数组思想是一种常见的动态规划优化方法。
假设有如下状态转移方程:
dp[i][j] = max(dp[i-1][j+1], dp[i-1][j-1]);
按照该状态转移方程,我们可以用二维数组保存其状态值,通过如下代码片段完成其状态的转移(这里仅做说明,不考虑边界情况):
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
dp[i][j] = max(dp[i-1][j+1], dp[i-1][j-1]);
}
}
int ans = dp[n][m];
考虑到每次状态的转移仅与上一行有关(或以及本行已更新的部分),我们可以将二维数组优化到使用一维数组保存。如下:
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
buf[i] = max(dp[j+1], dp[j-1]);
}
for(int j = 1; j <= m; j++){
dp[j] = buf[j];
}
}
int ans = dp[m];
如该代码片段所示,我们将原本二维的状态空间优化到了一维,对应的我们需要在每次状态转移过后进行依次循环次数为m的赋值操作。该操作不仅增加了代码量,还增加了程序的耗时。于是我们使用滚动数组,对其再次进行优化:
定义大小为2*m的数组为其状态空间:
int dp[2][m];
初始状态保存在dp[0][i]中。
设定两个int类型指针
int *src; //源指针
int *des; //目的指针
由于初始状态保存在dp数组的第0行中,初始时
src = dp[1];
des = dp[0];
按照状态转移方程进行状态转移
for(int i = 1; i <= n; i++){
swap(src, des); //交换源指针和目的指针
for(int j = 1; j <= m; j++){
des[j] = max(src[j+1], src[j-1]);
}
}
int ans = des[m];
如代码所示,我们在每次循环进行状态转移之前交换源数组和目的数组的指针,使程序能够正确的从源数组中转移状态到目的数组中。当状态转移完成时,新得到状态保存于目的数组中,但它在下一次循环的状态转移中又将变为源数组,于是我们在下次状态转移开始前再次交换源数组和目的数组指针,这就是滚动数组的工作原理。
滚动数组这个技巧不仅优化了原始的状态空间,还减少了循环次数节约了程序运行时间,同时对代码量的缩减也有很好的效果,是一个值得学习的小技巧。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!