http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2432
题意:给两个含正负数的序列,求最长公共上升子序列
思路:f[i][j]表示长度为j, a[i]结尾的子序列,相应地在b序列结尾出现最早的位置。那么f[i][j] = min{p | b[p] = a[i], f[k][j - 1] < p} // k < i && a[k] < a[i]
因为容易知道f[k][j] > f[k][j - 1], 所以可以O(n)进行转移。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define sqr(x) ((x) * (x))
#define two(x) (1 << (x))
#define Rep(i, s, n) for (int i = s; i < (n); ++i)
#define X first
#define Y second
typedef long long LL;
const int MAXN = 502;
const double eps = 1e-9;
const int INF = 1000000000;
int n, m, f[MAXN][MAXN], pre[MAXN][MAXN];
int lists[MAXN], a[MAXN], b[MAXN];
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
Rep(i, 0, n) scanf("%d", &a[i]);
scanf("%d", &m);
Rep(i, 0, m) scanf("%d", &b[i]);
memset(f, -1, sizeof(f));
Rep(i, 0, m) if (b[i] == a[0])
{
f[0][1] = i;
break;
}
Rep(i, 1, n)
{
int s = 0;
while (s < m && b[s] != a[i]) ++s;
if (s < m) f[i][1] = s; else continue;
Rep(j, 0, i) if (a[j] < a[i])
{
int p = s;
Rep(k, 1, i + 1) if (f[j][k] > -1)
{
while (p < m && (p <= f[j][k] || b[p] != a[i])) ++p;
if (p < m)
{
if (f[i][k + 1] > p || f[i][k + 1] == -1)
{
f[i][k + 1] = p;
pre[i][k + 1] = j;
}
} else break;
}
}
}
int ans = 0, tmp, last;
Rep(i, 1, n + 1) Rep(j, i - 1, n)
{
if (f[j][i] != -1)
{
last = j;
ans = i;
}
}
int L;
tmp = ans;
while (tmp > 0)
{
lists[tmp - 1] = a[last];
last = pre[last][tmp--];
}
printf("%d\n", ans);
Rep(i, 0, ans)
{
printf("%d", lists[i]);
if (i + 1 < ans) putchar(' '); else puts("");
}
if (T) puts("");
}
return 0;
}