[Codeforces Beta Round #10 D. LCIS] 从LIS、LCS到LCIS [dp学习笔记]

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值