AcWing 272. 最长公共上升子序列
考察:线性DP
最长上升子序列+最长公共子序列的综合版
思路:
要写这道题必须明白最长上升子序列+最长公共子序列的推导过程.
由最长上升子序列:
它的状态是由倒数第二个数字来分类,也就是说我们必须固定倒数第一个数字
由公共子序列:
有a[i]b[j]的有无来分类,f[i][j]代表1~i和1~j所构成的公共子序列
由此可得,f[i][j]也可以代表1~i和1~j所构成的公共子序列,但出于需要上升所以我们需要固定一个末尾,可以选a[i]也可b[j].假设选b.所以f[i][j]代表1~i和1~j所构成的公共子序列,且以b[j]结尾.
接下来就需要集合的划分:根据公共子序列的划分可分为有a[i]和无a[i].无a[i]就是f[i-1,j].有a[i]则隐含条件a[i] = b[j],注意这里不是简单的f[i-1,j-1].因为b[j-1]不一定<b[j].这里需要枚举所有的<j的取值.最后综合起来就是答案
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstdio> 5 using namespace std; 6 typedef long long ll; 7 const int N = 3010; 8 int a[N],b[N],f[N][N]; 9 int main() 10 { 11 int n; 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 14 for(int j=1;j<=n;j++) scanf("%d",&b[j]); 15 for(int i=1;i<=n;i++) 16 for(int j=1;j<=n;j++) 17 { 18 if(a[i]==b[j]) 19 { 20 for(int k=1;k<j;k++) 21 if(b[k]<b[j]) 22 f[i][j] = max(f[i-1][k]+1,f[i][j]); 23 } 24 else 25 f[i][j] = max(f[i-1][j],f[i][j]); 26 } 27 printf("%d\n",f[n][n]); 28 return 0; 29 }
由时间复杂度O(n3) 可知我们需要优化,第三层循环的本质就是找f[i-1][k]+1(k<j)的最大值.我们想到01背包的优化,利用二层循环的先后顺序设置变量maxn来记录j-1的最大值
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstdio> 5 using namespace std; 6 typedef long long ll; 7 const int N = 3010; 8 int a[N],b[N],f[N][N]; 9 int main() 10 { 11 int n,res = 0; 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 14 for(int j=1;j<=n;j++) scanf("%d",&b[j]); 15 for(int i=1;i<=n;i++) 16 { 17 int maxn = 1; 18 for(int j=1;j<=n;j++) 19 { 20 f[i][j] = max(f[i-1][j],f[i][j]); 21 if(a[i]==b[j]) f[i][j] = max(f[i][j],maxn); 22 if(a[i] >b[j]) maxn = max(f[i-1][j]+1,maxn); 23 } 24 } 25 for(int i=1;i<=n;i++) res = max(res,f[n][i]); 26 printf("%d\n",res); 27 return 0; 28 }
其实还可以优化到一维
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstdio> 5 using namespace std; 6 typedef long long ll; 7 const int N = 3010; 8 int a[N],b[N],f[N]; 9 int main() 10 { 11 int n,res = 0; 12 scanf("%d",&n); 13 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 14 for(int j=1;j<=n;j++) scanf("%d",&b[j]); 15 for(int i=1;i<=n;i++) 16 { 17 int maxn = 1; 18 for(int j=1;j<=n;j++) 19 { 20 if(a[i] >b[j]) maxn = max(f[j]+1,maxn);//maxn是上层的j更新 21 if(a[i]==b[j]) f[j] = max(f[j],maxn); 22 } 23 } 24 for(int i=1;i<=n;i++) res = max(res,f[i]); 25 printf("%d\n",res); 26 return 0; 27 }
2021.3.12 二刷 还是不会..完全没想到以b[j]固定为结尾的集合.如果是f[i][j]表示以i,j结尾的子序列.时间复杂度是O(n4)