Acwing 272.最长公共上升子序列 —— 单调序列+最长公共子序列

熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。

小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。

小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。

奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。

不过,只要告诉奶牛它的长度就可以了。

数列A和B的长度均不超过3000。

输入格式

第一行包含一个整数N,表示数列A,B的长度。

第二行包含N个整数,表示数列A。

第三行包含N个整数,表示数列B。

输出格式

输出一个整数,表示最长公共上升子序列的长度。

数据范围

1N3000,序列中的数字均不超过2e311

 1 #include <iostream>
 2 #include <cstdio>
 3 
 4 using namespace std;
 5 
 6 const int N = 3010;
 7 int f[N][N];
 8 int a[N], b[N];
 9 
10 int main()
11 {
12     int n;
13     cin >> n;
14 
15     for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
16     for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);
17 
18     for (int i = 1; i <= n; i ++ )
19     {
20         int maxv = 1;
21         for (int j = 1; j <= n; j ++ )
22         {
23             f[i][j] = f[i - 1][j];
24             if (a[i] == b[j]) f[i][j] = max(f[i][j], maxv);
25             if (a[i] > b[j]) maxv = max(maxv, f[i - 1][j] + 1);
26         }
27     }
28 
29     int res = 1;
30     for (int i = 1; i <= n; i ++ ) res = max(res, f[n][i]);
31     printf("%d", res);
32 
33     return 0;
34 }

这里的f[i, j]存放的是a序列的前i个字符和b序列前j个字符中最长的公共上升子序列,即a[i1~ i],b[1 ~ j]的序列中以b[j[结尾的序列的最大值。

两重循环解读

当a[i] != b[j]时这里的f[i, j]就是f[i - 1, j]也就是没有添加字符进去。

而当a[i] = b[j]时,这里的f[i, j]就是前面的所有f[i - 1, 1 ~ j] + 1及f[i - 1, j]里的最大值;

而这里的maxv是由三层循环优化而来原本的循环体为

1 for (int i = 1; i <= n; i ++ )
2     for (int j = 1; j <= n; j ++ )
3     {
4         f[i][j] = f[i - 1][j];
5         if (a[i] == b[j])
6             for (int k = 1; k <= j; k ++ )
7                 if (a[i] > b[k])
8                     f[i][j] = max(f[i - 1][k] + 1, f[i][j]);
9     }

第三层循环是求得是从f[i - 1, 1 ~ j]中能将a[i]插入进去的子序列的最大值,因此我们可以在第二层循环时求出

即使用一个maxv中间变量,对于每一个a[i] > b[j]的值我们对其求一个max并记录下来,这样我们只需在下一次循环时使用前面循环所求的maxv即可。

posted @ 2020-12-22 03:00  筱翼深凉  阅读(146)  评论(0编辑  收藏  举报