Description
This problem differs from one which was on the online contest.
The sequence a1, a2, ..., an is called increasing, if ai < ai + 1 for i < n.
The sequence s1, s2, ..., sk is called the subsequence of the sequence a1, a2, ..., an, if there exist such a set of indexes 1 ≤ i1 < i2 < ... < ik ≤ n that aij = sj. In other words, the sequence s can be derived from the sequence a by crossing out some elements.
You are given two sequences of integer numbers. You are to find their longest common increasing subsequence, i.e. an increasing sequence of maximum length that is the subsequence of both sequences.
Input
The first line contains an integer n (1 ≤ n ≤ 500) — the length of the first sequence. The second line contains n space-separated integers from the range [0, 109] — elements of the first sequence. The third line contains an integer m (1 ≤ m ≤ 500) — the length of the second sequence. The fourth line contains m space-separated integers from the range [0, 109] — elements of the second sequence.
Output
In the first line output k — the length of the longest common increasing subsequence. In the second line output the subsequence itself. Separate the elements with a space. If there are several solutions, output any.
Examples
Input
7 2 3 1 6 5 4 6 4 1 3 5 6
Output
3 3 5 6
Input
5 1 2 0 2 1 3 1 0 1
Output
2 0 1
本题给出两个序列A、B,并要求出两个序列的最长公共上升子序列。求最长上升子序列和最长公共子序列都是比较经典的题目。
对于最长上升子序列,一个常见的算法是定义dp[i]为以a[i]为结尾的最长上升子序列,
则: dp[i]= max{ dp[k] | 1<=k<i, a[i]>a[k] } + 1
对于最长公共子序列,一个常见的算法则是定义dp[i][j]为 a[1...i]和b[1..j]所包含的最长公共子序列,
则: (1) 当 a[i]!=b[j]时, dp[i][j]=max(dp[i-1][j],dp[i][j-1])
(2) 当 a[i]==b[j]时,dp[i][j]=dp[i-1][j-1]+1
而LCIS,则同时包含了LIS和LCS的特点。
一开始的时候,我打算把dp[i][j]定义为以a[i]和b[j]为结尾的LCS,结果发现这种定义方法并不理想,因为对于a[i]!=b[j]的状态,dp[i][j]=0,而对于a[i]==b[j]的状态,dp[i][j]=max{ dp[m][n] | m<i, n<j, dp[m][n]!=0, b[n]<b[j] },时间复杂度较大。
一种改进的方式是:
定义dp[i][j]为 a[1...i]和b[1...j]包含的以b[j]为结尾的LCS(不再要求以a[i]结尾),这样状态转移方程可以写成:
(1)当a[i]!=b[j]时, dp[i][j]=dp[i-1][j]
(2)当a[i]==b[j]时,dp[i][j]=max{ dp[i-1][k] | k<j, b[k]<b[j] }+1
这种改进的方法也并不难得出,因为LIS和LCS就是一个要定义成以某个元素结尾,而另一个不要求,混合两种特点以后就得到了改进后的LCS算法
剩下的问题就是怎样保存得到序列,方法也很简单,当a[i]==b[j]要更新dp[i][j]的值的时候,开一个数组f,把最后最优的k记录下来,就是让f[j]=kbest,这样其实就构成了一个链表,顺着这个链表就能找到最优序列的每一个元素,不过这个链表是逆序的,输出的时候要再反过来
交的时候没弄好边界条件,WA了几发,实在是有点蛋疼 T_T
AC代码
#include<bits/stdc++.h>
//#define LOCAL_PC
using namespace std;
int a[507];
int b[507];
int dp[507][507];
int f[507];
int n, m;
int main()
{
#ifdef LOCAL_PC
freopen("E:/1.txt", "r", stdin);
#endif // LOCAL
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
cin >> m;
for (int i = 1; i <= m; ++i) {
cin >> b[i];
f[i] = 0;
}
for (int j = 1; j <= m; ++j)
{
int t = 0;
for (int i = 1; i <= n; ++i)
{
if (a[i] == b[j]) t = 1;
dp[i][j] = t;
}
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
int t = 0;
for (int k = 1; k <= i; ++k)
{
if (a[k] == b[j]) { t = 1; break; }
}
dp[i][j] = t;
}
}
for (int i = 2; i <= n; ++i)
{
for (int j = 2; j <= m; ++j)
{
if (a[i] != b[j]) dp[i][j] = dp[i - 1][j];
if (a[i] == b[j])
{
int ans = 0;
for (int k = 1; k<j; ++k)
{
if (b[j] <= b[k]) continue;
if (dp[i - 1][k]>ans)
{
ans = dp[i - 1][k];
f[j] = k;
}
}
dp[i][j] = ans + 1;
}
}
}
int ansm = 1, t = dp[n][1];
for (int j = 2; j <= m; ++j)
{
if (dp[n][j]>t)
{
t = dp[n][j];
ansm = j;
}
}
cout << dp[n][ansm] << endl;
if (dp[n][ansm] == 0) return 0;
vector<int> ansvec;
for (int i = ansm; i != 0; i = f[i])
ansvec.push_back(b[i]);
for (int i = ansvec.size() - 1; i >= 0; --i)
cout << ansvec[i] << ' ';
return 0;
}