poj 2127

题目要求出最长公共上升子序列,我们知道,求最长公共子序列的算法是O(n^2)的,求最长上升子序列是O(n*(logn))的。所以这两个问题合在一起,复杂度不会低于O(n*logn)

由于数据给到500,所以O(n^3) 的算法是可以过的。

定义max[i][j]为A串匹配到i,B串匹配到j时的最长公共上升子序列的长度。

若A[i]!=B[j] 那么max[i][j]=MAX{max[i'][j']|i'<i,j'<j}

若A[i]==B[j] 那么max[i][j]=MAX{max[i'][j']+1|i'<i,j'<j,A[i']<A[i],B[j']<B[j]}

这一看就是一个O(n^4) 的算法,但是我们会发现max[i][j]长度会增加1的时候都是在A[i]==B[j]情况下,那么我们应该考虑如何应用这个性质呢。

所以我们以A串为基准,重新定义max[i][j]:以A[i]为基准匹配串末尾时(A[i]必选),匹配到B[j]时的最长公共上升子序列的长度

若A[i]!=B[j] 那么max[i][j]=max[i][j-1]

若A[i]==B[j] 那么max[i][j]=MAX{max[i'][j-1]+1|i'<i,A[i']<A[i]}

通过这样的定义我们保证了,对于任何max[i][j],max[i][j]里面存放的是已当前A[i]为匹配的串的最后一个元素时的最大长度

我们可以看到这个方程的复杂度只有O(n^3) ,对于j的循环就没有必要了。

代码:

#include<iostream>
#include<fstream>

using namespace std;

int dp[501][501];
int a[501],b[501];
int n,m;

void print(int s,int t){
	if(t==0||s==0) return;
	if(a[s]!=b[t])
		print(s,t-1);
	else
	{
		int minn=0;int j;
		for(int k=1;k<s;k++)
			if(a[k]<a[s]&&minn<dp[k][t-1])
			{
				minn=dp[k][t-1];
				j=k;
			}
		if(minn!=0)
			print(j,t-1);
		cout<<a[s]<<' ';
	}
}


void read(){
//	ifstream cin("in.txt");
	int i,j,k;
	while(cin>>n)
	{
	for(i=1;i<=n;i++)
		cin>>a[i];
	cin>>m;
	for(j=1;j<=m;j++)
		cin>>b[j];
	memset(dp,0,sizeof(dp));


	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		{
			if(a[i]!=b[j])
				dp[i][j]=dp[i][j-1];
			else
			{
				int s=0;
				for(k=1;k<i;k++)
					if(a[k]<a[i]&&s<dp[k][j-1])
					{
						s=dp[k][j-1];
				
					}
				dp[i][j]=s+1;
			}
		}
	int res=0;
	for(i=1;i<=n;i++)
	{
		if(res<dp[i][m])
		{
			res=dp[i][m];
			j=i;
		}
	}
	cout<<res<<endl;
	if(res!=0)
	{
		print(j,m);
		
	}
	cout<<endl;
	}
}

int main(){
	read();
	return 0;
}

posted on 2011-05-02 14:41  宇宙吾心  阅读(692)  评论(0)    收藏  举报

导航