1.从滚动数组开始
在写动态规划的时,我们有时会发现,我们的代码在世间上很好,但在空间上却存在一些问题,这是因为我们使用了二维数组,一位记录时间,一维记录空间消耗。However,我们不难发现,我们其实没有必要记录时间戳,当我们更新当前节点时,所有的数据都是在之前被更新的,都具有转移的条件。
例题 疯狂的采药
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10;
long long w[100010],val[100010];
long long dp[N];
int main(){
int t,m;
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>w[i]>>val[i];
}
for(int i=1;i<=m;i++)
for(int j=0;j<=t;j++){
if(j>=w[i]){
dp[j]=max(dp[j-w[i]]+val[i],dp[j]);
}
else
dp[j]=dp[j];
}
cout<<dp[t]<<endl;
return 0;
}
2.时间->空间
当我们在动态规划中要调用一个不会更改的值的时候,我们就可以将其预处理进入一个表格,用以空间换时间的方式减少时间复杂度。减小在算法中对同一段或相似区间的开销大的重复性运用。
更特殊的,对于预处理,前缀和是一种实用的算法。前缀和是一个经典的时间压缩技巧,前缀和适用要求满足结合律的,如加法,乘法,异或。前缀和的核心思路是将区间[x,y],看做区间[1,y]-区间[1-x-1],这样的话,我们只需要从一到n循环一遍,做预处理即可。
2.5用整体取代局部
基于区间求和的思想,我们可以在dp上做更多的优化。算法优化的本质是将同样的事情用尽量少的时间完善,这就允许我们更多的利用整体,统一计算的思维。
就比如说,假设我们发现值为1时的情况和值为2时一样,我们就可以将两者合并考虑,从而节省时空复杂度。