当然,原地滚动永远是最优的(比如:经典的背包问题)
滚动数组适用的条件
打个比方,某dp的转移方程如下
f[i][x] = min(f[i-1][a],f[i-2][b],...,f[i-j][k]
i
的状态仅仅取决于(i-1,...,i-j
)这j个状态,而与更之前的状态无关
那么更之前的状态我们可以不用保存, 只需要保存待转移的状态
和转移需要的状态
(这里状态指的是某维度,如 i,i-1)
所以,我们必须能够确定转移需要的状态
的最大数目,才能使用滚动数组优化
滚动数组的几种写法
特殊情况,只取决于上一个维度的状态
- 利用相邻两个数奇偶性不同这一性质 进行转移
例子:acwing292炮兵阵地的转移方程f[i & 1][j][k] = max(f[i & 1][j][k], f[i - 1 & 1][u][j] + cnt[c]);
- 把数组当作变量,交换即可
memcpy(tmp,f[2],sizeof f[2]);
memcpy(f[2],f[1],sizeof f[1]);
memcpy(f[1],tmp,sizeof f[1]);
注:memcpy是对数组元素逐个拷贝的,所以会很慢,此外
std::swap()
对数组进行交换也是逐个拷贝的,运行效率比memcpy还要慢
当前状态取决于前x个的状态
随着dp的遍历,最后一个状态会变成无用的状态,该状态的空间被拿来重复利用,问题在于如何确定下标,如何减少复杂度
1.取模运算
假设 算上待转移的状态共有x个状态,那么我们用 i mod x得到对应下标
(0...x-1)
因为这x个状态是连续的,所以必然可以不重不漏的找到对应下标
2.把数组当成元素,进行交换
末尾的状态 i - x将来用不到,于是把所有状态的值向前平移,腾出位子
不过这样实在是太低效了,又要拷贝元素什么的
但是!! 使用vector就可以避免拷贝.swap对容器和适配器类型是进行交换指针,速度非常快
不过对多维数组的定义比较麻烦
比如自己的某题的定义vector<vector<vector<int>>>f(2,vector<vector<int>>(1<<M,vector<int>(1<<M,0)));
所以,如果对下标拿捏不定,不如就用vector来搞定。
嫌vector定义太麻烦,就用取模运算咯
点个赞再走? 靓仔