滚动数组的几种写法

当然,原地滚动永远是最优的(比如:经典的背包问题)

滚动数组适用的条件

打个比方,某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)
所以,我们必须能够确定转移需要的状态的最大数目,才能使用滚动数组优化

滚动数组的几种写法

特殊情况,只取决于上一个维度的状态

  1. 利用相邻两个数奇偶性不同这一性质 进行转移
    例子:acwing292炮兵阵地的转移方程
    f[i & 1][j][k] = max(f[i & 1][j][k], f[i - 1 & 1][u][j] + cnt[c]);
  2. 把数组当作变量,交换即可
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定义太麻烦,就用取模运算咯

点个赞再走? 靓仔

上一篇:EndPoints+Ingress+控制器(DaemonSet+StatefulSet+Job+cronJob)


下一篇:ingress-nginx 部署使用