关于动态规划的线性DPの一己之见

记录关于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模板:

  1. 第一层 for 枚举物品的组数
  2. 第二层 for 枚举物品的容量
  3. 第三层 for 进行决策

而当遇到求方案数时,也是可以由背包得出经验,不外乎个数有限与无限的区别,那么其实就是01背包/完全背包:

扫描二维码关注公众号,回复: 11138487 查看本文章
// 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. 其他问题总结

  1. 回文串: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 i f s [ i ] = = s [ j ] if s[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])

  2. 题目:多个串的匹配,考虑 d p [ i ] [ j ] dp[i][j] 为s1的前i个字符与s2的前j个字符。

  3. 题目:n个箱子选前k小的价值和,例如 d p [ i ] [ k ] dp[i][k] 为前i个箱子的价值为k的方案。

  4. 题目:对于挑选方案可以按照背包思想,根据题目划分为01背包或者完全背包,再考虑是否是分组背包(有决策层)。

  5. 题目:对于数列型题目,可以考虑类似于LIS或者LCS来思考,以 d p [ i ] dp[i] 表示以当前元素结尾的状态值,若是元素范围少,可考虑为 d p [ i ] [ j ] dp[i][j] a [ i ] a[i] 结尾且和为 j j 或者差为 j j 等等状态,把第二维作为对于给出条件的一种枚举。

以上,其他内容等遇到再补充。

原创文章 96 获赞 109 访问量 4万+

猜你喜欢

转载自blog.csdn.net/JiangHxin/article/details/105184169