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  }
TLE暴力

 

 

      由时间复杂度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)

posted @ 2021-02-06 18:30  acmloser  阅读(68)  评论(0编辑  收藏  举报