Acwing 272.最长公共上升子序列 —— 单调序列+最长公共子序列
熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。
小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。
小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。
奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。
不过,只要告诉奶牛它的长度就可以了。
数列A和B的长度均不超过3000。
输入格式
第一行包含一个整数N,表示数列A,B的长度。
第二行包含N个整数,表示数列A。
第三行包含N个整数,表示数列B。
输出格式
输出一个整数,表示最长公共上升子序列的长度。
数据范围
1≤N≤3000,序列中的数字均不超过2e31−1
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即可。