DP知识点总结2 子序列类线性DP(实时更新)
一、万恶之源——最长上升子序列
https://www.acwing.com/problem/content/1019/
1.经典DP解法
设置f数组,以子序列的最后一个数i划分DP区间,求得最优解。注意最后要for循环扫一下。
代码略
2.二分查找
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 using namespace std; 6 int t; 7 const int maxn=105; 8 int w[maxn]; 9 int f[maxn]; 10 int main(){ 11 12 scanf("%d",&t); 13 while(t--){ 14 memset(w,0,sizeof(w)); 15 memset(f,0,sizeof(f)); 16 int n; 17 scanf("%d",&n); 18 for(int i=1;i<=n;i++) scanf("%d",&w[i]); 19 f[1]=w[1]; 20 int cnt=1; 21 for(int i=2;i<=n;i++){ 22 if(w[i]<f[cnt]) f[++cnt]=w[i]; 23 else{ 24 int pos=lower_bound(f+1,f+cnt+1,w[i],greater<int>())-f; 25 f[pos]=w[i]; 26 } 27 } 28 int cnt1=cnt; 29 reverse(w+1,w+n+1); 30 memset(f,0,sizeof(f)); 31 f[1]=w[1]; 32 cnt=1; 33 for(int i=2;i<=n;i++){ 34 if(w[i]<f[cnt]) f[++cnt]=w[i]; 35 else{ 36 int pos=lower_bound(f+1,f+cnt+1,w[i],greater<int>())-f; 37 f[pos]=w[i]; 38 } 39 } 40 cnt=max(cnt,cnt1); 41 printf("%d\n",cnt); 42 } 43 44 45 46 return 0; 47 48 }
二、各类变体
1.排列数的最长公共子序列
https://www.luogu.com.cn/problem/P1439
因为是排列数,所以可以将之转化为求最长上升子序列问题,从而将复杂度降低至O(nlogn)
2.朴素最长公共子序列
参考进阶指南P264.通常对于这样的题目及其变体,f 数组应开为 f [ i , j ]
3.最长公共上升子序列
https://www.acwing.com/problem/content/274/
首先分析状态表示,其实这个相当于最长上升子序列和最长公共子序列的结合,对于最长上升子序列,f[i][j]表示是前i位,且最后一位是j的上升子序列,因为上升需要比较。
而最长公共子序列中f[i][j]表示的是前第一个序列的前i位,第二个序列的前j位组成的最长公共子序列
那么本题f[i][j]表示的就是第一个序列前i位,第二个序列前j位,且结尾是第二个序列第j位的最长公共上升子序列
本题的一个重要思想是,通常状态计算的过程中,是看最后一个位置,但这道题最后一个位置j是给定的,那么就看倒数第二个位置,以其进行划分。
第二个难点是要简化时间复杂度还需要对代码进行一个等价变形
4.双上升子序列划分
https://www.luogu.com.cn/problem/P1410
5.双子序列最大和
https://www.luogu.com.cn/problem/P2642
6.dilworth定理
https://blog.csdn.net/numb_ac/article/details/103396897