做dp前先要了解dp分析法,不了解的朋友们去看之前的帖子
闫氏DP分析法
核心思想:从集合角度来分析DP问题
dp问题,在有限集合中求最值
为什么用dp?
因为集合中数量太多
dp为什么能解决数量太多的问题
用两个阶段
一阶段状态表示
化零为整状态表示----不是一个元素一个元素枚举,每次枚举一类元素,化成子集,用一个状态来表示
状态表示f(i)分为:集合定位和集合属性
二阶段状态计算
状态计算,化整为零
例如需要求f(i),f(i)是最大值,然后我们只需要将f(i)划分成集合
判断每个子集的最大值,哪个更大,哪个就是所有集合最大值
子集一般满足两个原则(不遗漏,不重复),有时候可以重复,但一定不能遗漏
集合的划分依据:寻找最后一个不同点
dp问题很多种
选择问题,线性DP,区间DP
这次是线性dp
点击主页更多dp写法
样题
最长公共子序列
给定两个长度分别为 N 和 M的字符串 A和 B,求既是 A的子序列又是 B的子序列的字符串长度最长是多少。
输入格式
第一行包含两个整数 N和 M。
第二行包含一个长度为 N的字符串,表示字符串 A。
第三行包含一个长度为 M的字符串,表示字符串 B。
字符串均由小写字母构成。
输出格式
输出一个整数,表示最大长度。
数据范围
1≤N,M≤1000
输入样例:
4 5
acbd
abedc
输出样例:
3
题目解析
最长公共子序列是无需连续的,例如样题中的两个序列
acbd
abedc
他们的最长公共子序列是abd
acbd
abedc
如果是最长子串,就是需要连续的
看到数据大小,联想y总的时间复杂度分析图
很容易想到是dp来做
闫氏dp分析法
先画思维导图,不清楚闫氏dp分析法的可以看之前的文章
在取max时,我们会发现,不选A[i],不选B[j]这种情况我们可以舍弃
因为都不选无法让子序列变得更长
而且,我们在选的时候,除了第四种情况,a[i],b[j]一定选了
其他情况实际上无确定a[i]是不是在最长公共子序列里
例如f(i,j-1),他的值实际上是a[i]到b[j-1]的最长公共子序列
在这个值里,不一定选择了a[i],但是这对我们的结果没影响
因为我们只需要最大长度即可
我们用一个图来讲解一下,例如两个字符串
A B C B D A B
B D C A B C
答案是
A B C B D A B
B D C A B C
图来源:b战:邋遢大哥233
带绿色箭头的,就是a[i]b[j]相等的情况
如果a[i]b[j]相等,就用我们思维导图上的f(i-1,j-1)
也就是f[i-1][j-1]+1,然后跟左边的f[i][j-1]和上边的f[i-1][j]做比较
取最大值
f[n][m]就是我们最后的答案,也就是4
#include<iostream>
using namespace std;
int f[1010][1010];
char a[1010],b[1010];
int main(){
int n,m;
//a+1的效果是从a[1]开始读字符串
cin>>n>>m>>a+1>>b+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
//判断上边和左边
f[i][j]=max(f[i][j-1],f[i-1][j]);
//判断a[i],b[j]是否相等
//其实这里不需要max特判也可以通过
//只要a[i]==b[j]
//则f[i-1][j-1]+1一定大于等于f[i][j-1]或者f[i-1][j]
//但是我不知道为什么,评论区有大神可以教教我
//等我学会了再更新
if(a[i]==b[j])f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
}
cout<<f[n][m];
return 0;
}
2024/3/25 18:59更新
已经学会为什么只要a[i]==b[j]
则f[i-1][j-1]+1一定大于等于f[i][j-1]或者f[i-1][j]
因为f[i-1][j-1]+1,本身表示的就是f[i][j],这时,对于a,b(标红表示+1序列)序列来说
他们都是在末尾补了一个字符,i也+1,j也+1
那无论f[i][j-1]还是f[i+1][j],其中的a,b序列,都只能是和a,b序列相等或者是其子序列
例如a[i-1]一定是a[i]子序列,b[j-1]一定是b[j]子序列
a[i]b[j]的子序列的公共子序列长度
可能等于序列本身的公共子序列长度
但一定不会大于a[i]b[j]的公共子序列长度
所以a[i]==b[j]时,f[i-1][j-1]+1表示f[i][j],我们直接取序列本身的公共子序列长度即可
严谨版下图,比较难看懂,感兴趣的同学可以看看
代码优化
#include<iostream>
using namespace std;
int f[1010][1010];
char a[1010],b[1010];
int main(){
int n,m;
cin>>n>>m>>a+1>>b+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i]==b[j])f[i][j]=f[i-1][j-1]+1;
else f[i][j]=max(f[i][j-1],f[i-1][j]);
}
}
cout<<f[n][m];
return 0;
}
下篇继续做dp
动态规划的狂
dp的王