1.哪些可以抽象为背包类的DP:
一般是涉及到有限制的选择问题,可以用背包问题来做
2.DP的复杂度计算:一般=状态数量 x 转移的计算量
3.关于DP的优化,分为时间和空间两个方面上(有时候只能优化空间,有时候时间和空间都可以优化)
优化时间:
一般是优化第三重循环:循环决策(子集),然后有一些巧妙的方法可以去掉第三重循环:循环决策(这个关于第几重循环后面会补充,先完整地看下去)
比如:
推导出的状态计算公式:
f[i][j] = f[i-1][j] + f[i-1][j-v] + f[i-1][j-2v] + f[i-1][j-3v]+.....
试着写出f[i][j-v] ,就是等量代换,把第二维的j换成j-v
f[i][j-v] = f[i-1][j-v] + f[i-1][j-2v] + f[i-1][j-3v]+....
此时我们发现f[i][j-v] 与 f[i][j] 除了第一个,其余后面的一样,这样我们可以优化f[i][j]的计算
此时:
f[i][j] = f[i-1][j] + f[i][j-v]
优化后,把O(M)的计算量变成了O(1) , 也就是去掉了第三重循环:循环决策
这样就优化了时间
优化空间:
一般是通过代码变形来优化(代码本身的逻辑),而不是通过题目本身来优化
先写出朴素写法,然后判断是否可以去掉第一维(代码逻辑要一致),只保留第二维来做到优化空间
这个时候有个需要注意的点是:去掉第一维后,要判断第二重循环应从小到大还是从大到小
通俗地讲:朴素写法状态计算时
若要用到上一层的状态,第二层循环就从大到小枚举
若用的是本层,第二层循环就从小到大枚举
比如01背包问题的模板:
先写出朴素做法,再通过代码等价变换来优化空间
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j];
if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
........................................................................................
计算时第i层f[i][j]时,发现要用到i-1层的f[i-1][....]
所以应该从大到小枚举第二重循环
优化后为:
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
最后补充一下上面遗留的第几重循环:
背包问题优化空间之后,循环顺序是一定不能变的
第一重循环:一定是循环物品
第二重循环:一定是循环体积
第三重循环:一定是循环决策(子集)
当然了这里提到的物品、体积都是背包问题模板中的说法,做题目时,要抽象出来"物品"、"体积”
最后欢迎大家补充或者指正!!!