记录关于DP的历程,随着刷题而更新。
个人向,仅自己回顾用,若是有讲不清楚的地方欢迎回复私信交流。
一般情况下,动态规划的解题步骤是:
第一步:根据原问题和子问题来确定状态(dp数组表示什么东西)
第二步:根据状态确定状态转移方程(怎样求解dp数组 递推?dfs?)
第三步:确定要不要优化和编程实现方式 (单调队列?线段树?)
先学习简单的线性DP,其实关于线性DP的话,不外乎这几种。
1. LIS(最长上升子序列)
其实LIS的话,是dp入门必不可少的题目,正常时间算法时间复杂度 n^2,可用二分或树状数组优化达到 nlogn,用 dp[i] 维护 i 位置的最小值,最后dp数组的长度即最长上升子序列的长度,但是具体的过程和内容却无法求。
len=1;
dp[1]=a[1];
for(int i=2;i<=n;i++){
if(dp[len]<a[i]) dp[++len]=a[i];
else{
ll k=lower_bound(dp+1,dp+len+1,a[i])-dp;
dp[k]=a[i];
}
}
2. LCS(最长公共子序列)
特殊的,假设序列a和b是1-n的排列组合,那么可以离散化a序列,那么求出b的最长上升子序列就是a与b的最长公共子序列。
tip: 因为最长公共子序列是按位向后比对的,所以a序列每个元素在b序列中的位置如果递增,就说明b中的这个数在a中的这个数整体位置偏后,可以考虑纳入LCS——那么就可以转变成nlogn求用来记录新的位置的离散化后的LIS。
3. 背包问题
背包问题就是以01背包为基础进行扩展的一系列问题。
当求最优/最大利益时,假设 M 为物品组数,T 为最大容量,a[i] 为每一件物品的体积,b[i] 为每一件物品的价值,c[i] 为消耗的容量不大于i的情况下所获得的最大收益,则有 :
// 1. 逆序 01 背包
for(int i=1;i<=M;i++)
for(int j=T;j>=a[i];j--)
c[j]=max(c[j],c[j-a[i]]+b[i]);
//2. 正序 完全 背包
for(int i=1;i<=M;i++)
for(int j=a[i];j<=T;j++)
c[j]=max(c[j],c[j-a[i]]+b[i]);
而其他的情况多重背包不过是多了一个把物品拆分/合并的过程,而分组背包不过是多了一个决策过程,经过这些的猜想,不难得出背包的dp模板:
- 第一层 for 枚举物品的组数
- 第二层 for 枚举物品的容量
- 第三层 for 进行决策
而当遇到求方案数时,也是可以由背包得出经验,不外乎个数有限与无限的区别,那么其实就是01背包/完全背包:
// 1. 逆序 01 背包
for(int i=1;i<=M;i++)
for(int j=T;j>=a[i];j--)
c[j]+=c[j-b[i]];
//2. 逆序 完全 背包
for(int i=1;i<=M;i++)
for(int j=a[i];j<=T;j++)
c[j]+=c[j-b[i]];
可以发现,不过是状态转移的方程改变了而已。
4. dirworth定理
求最长上升序列的长度等于求最长不上升序列的划分个数。
5. 其他问题总结
-
回文串:dp[i][j] 为字符串 的第 i 个字符到第 j 个字符的最长回文子序列长度。
特殊的: d p [ i ] [ j ] = d p [ i + 1 ] [ j − 1 ] + 2 dp[i][j]=dp[i+1][j−1]+2 dp[i][j]=dp[i+1][j−1]+2 i f s [ i ] = = s [ j ] if s[i]==s[j] ifs[i]==s[j]
一般的: d p [ i ] [ j ] = m a x ( d p [ i + 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j]= max(dp[i+1][j],dp[i][j-1]) dp[i][j]=max(dp[i+1][j],dp[i][j−1]) -
题目:多个串的匹配,考虑 d p [ i ] [ j ] dp[i][j] dp[i][j]为s1的前i个字符与s2的前j个字符。
-
题目:n个箱子选前k小的价值和,例如 d p [ i ] [ k ] dp[i][k] dp[i][k] 为前i个箱子的价值为k的方案。
-
题目:对于挑选方案可以按照背包思想,根据题目划分为01背包或者完全背包,再考虑是否是分组背包(有决策层)。
-
题目:对于数列型题目,可以考虑类似于LIS或者LCS来思考,以 d p [ i ] dp[i] dp[i]表示以当前元素结尾的状态值,若是元素范围少,可考虑为 d p [ i ] [ j ] dp[i][j] dp[i][j] 以 a [ i ] a[i] a[i] 结尾且和为 j j j 或者差为 j j j 等等状态,把第二维作为对于给出条件的一种枚举。
以上,其他内容等遇到再补充。