[codevs2185]最长公共上升子序列
试题描述
小沐沐说,对于两个串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 大力过去了。。。