[codevs2185]最长公共上升子序列
[codevs2185]最长公共上升子序列
试题描述
熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们要研究最长公共上升子序列了。
小沐沐说,对于两个串A,B,如果它们都包含一段位置不一定连续的数字,且数字是严格递增的,那么称这一段数字是两个串的公共上升子串,而所有的公共上升子串中最长的就是最长公共上升子串了。
奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子串。不过,只要告诉奶牛它的长度就可以了。
小沐沐说,对于两个串A,B,如果它们都包含一段位置不一定连续的数字,且数字是严格递增的,那么称这一段数字是两个串的公共上升子串,而所有的公共上升子串中最长的就是最长公共上升子串了。
奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子串。不过,只要告诉奶牛它的长度就可以了。
输入
第一行N,表示A,B的长度。
第二行,串A。
第三行,串B。
第二行,串A。
第三行,串B。
输出
输出长度。
输入示例
4 2 2 1 3 2 1 2 3
输出示例
2
数据规模及约定
1<=N<=3000,A,B中的数字不超过maxlongint
题解
首先果断想一个 n2logn 的做法:令 f(i, j) 表示考虑了第一个串的前 i 位,第二个串的前 j 位,最长公共上升子序列的末位是 B[j] 的最长公共上升子序列长度;那么如果 A[i] = B[j],就是所有满足 B[k] < B[j] 且 k < j 的 f(i-1, k) 转移到 f(i, j),否则 f(i, j) = f(i-1, j),于是我们可以随着 j 的递增将所有 f(i-1, j) 扔进树状数组,每次转移时查询一下前缀最大值就好了。
然后发现 n2 做法更 SB:由于我们每次在树状数组上询问的前缀都是 [1, A[i] ](因为只有当 A[i] = B[j] 时才会有转移),所以直接用一个变量存满足 B[k] < A[i] 且 k < j 的 f(i-1, k) 的最大值就好了。注意这里要用滚动数组,滚动数组用起来很方便,因为所有没更新过的 f(i, j) 就等于 f(i-1, j),所以 i 每加 1,直接在上一个版本的滚动数组上做就行了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 3010 int n, A[maxn], B[maxn], f[maxn]; int main() { n = read(); for(int i = 1; i <= n; i++) A[i] = read(); for(int i = 1; i <= n; i++) B[i] = read(); int ans = 0; for(int i = 1; i <= n; i++) { int mx = 0; for(int j = 1; j <= n; j++) { if(B[j] < A[i]) mx = max(mx, f[j]); if(A[i] == B[j]) f[j] = max(f[j], mx + 1); ans = max(ans, f[j]); } } printf("%d\n", ans); return 0; }
当然这题 n2logn 也是能过的。据说有人 n3 大力过去了。。。