hdu 1423最长公共递增子序列

                                 Greatest Common Increasing Subsequence
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 2881    Accepted Submission(s): 901
Problem Description
This is a problem from ZOJ 2432.To make it easyer,you just need output the length of the subsequence.
 
Input
Each sequence is described with M - its length (1 <= M <= 500) and M integer numbers Ai (-2^31 <= Ai < 2^31) - the sequence itself. 
 
Output
output print L - the length of the greatest common increasing subsequence of both sequences. 
 
Sample Input
1


5
1 4 2 5 -12
4
-12 1 2 4
 
Sample Output
2
 
Source
ACM暑期集训队练习赛(二) 
 
Recommend

lcy

对于做dp的人而言,规划处最优子结构是解决一切题目的第一步,二此题的最优越子结构规划一下,DP[i][j] 为序列1前i个元素和序列2前j个元素最长公共上升子序列多长。
那么这个时候初始值初始化为0的话,碰到序列1和序列2相等的情况只要依靠相等位置前的序列来得出状态即可,更新完整个dp数组。
代码如下:
#include<stdio.h>
#include<string.h>
#define N 505
int dp[N][N],a[N],b[N];
int max(int a,int b)
{
	return a>b?a:b;
}
int  main()
{
	int t,i,j,k,l,n,m,ans;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		scanf("%d",&m);
		for(i=1;i<=m;i++)
			scanf("%d",&b[i]);
		memset(dp,0,sizeof(dp));
		ans=0;
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=m;j++)
			{
				if(a[i]==b[j])
				{
				   for(k=0;k<i;k++)//这里不是很对
					   if(a[k]<a[i])
					   {
						   for(l=0;l<j;l++)//还有这里
							   dp[i][j]=max(dp[i][j],dp[k][l]+1);
					   }
				}
				ans=max(ans,dp[i][j]);
			}
		}
		printf("%d\n",ans);
		if(t)
			printf("\n");
	}
	return 0;
}
/*
数据弱,下面一组应该为3,但输出2
1
5
-12 1 4 2 5
4
-12 1 2 4
*/

虽然问题是解决了,但是复杂度还是过高,有O(n^4)。现在我们可以想一想,外层循环是代表的是序列1我们就可以想到先是序列1前1项和序列2最长公共上升子序列长度,再循环一次,就是序列1前2项和序列2最长公共子序列长度。
那么既然每次循环能够推动到后状态,那么现在我们改变一下原来的代码。
#include<stdio.h>
#include<string.h>
#define N 505
int dp[N],a[N],b[N];
int max(int a,int b)
{
	return a>b?a:b;
}
int  main()
{
	int t,i,j,k,n,m,ans;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		scanf("%d",&m);
		for(i=1;i<=m;i++)
			scanf("%d",&b[i]);
		memset(dp,0,sizeof(dp));
		ans=0;
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=m;j++)
			{
				if(a[i]==b[j])
				{
					dp[j]=1;
				   for(k=0;k<j;k++)
					   if(b[k]<b[j]&&dp[j]<dp[k]+1)
					     dp[j]=dp[k]+1;
				}
				ans=max(ans,dp[j]);
			}
		}
		printf("%d\n",ans);
		if(t)
			printf("\n");
	}
	return 0;
}

这个时候我们又可以想到,既然每次的后状态都依赖到了前状态,那么我是不是可以记录住前状态在DP[i][j]的数组当中然后,而不需要每次遇到相同的时候我们都从前状态当中去找那个形成的最长序列,答案就是用一个值来维护更新。改变代码为:
#include<stdio.h>
#include<string.h>
#define N 505
int dp[N][N],a[N],b[N];
int max(int a,int b)
{
	return a>b?a:b;
}
int  main()
{
	int t,i,j,k,n,m,ans,ma;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		scanf("%d",&m);
		for(i=1;i<=m;i++)
			scanf("%d",&b[i]);
		memset(dp,0,sizeof(dp));
		ans=0;
		for(i=1;i<=n;i++)
		{
			ma=0;
			for(j=1;j<=m;j++)
			{
				dp[i][j]=dp[i-1][j];
				if(a[i]>b[j]&&dp[i-1][j]>ma)
					ma=dp[i-1][j];
				if(a[i]==b[j])
				   dp[i][j]=ma+1;
			}
		}
		for(i=1;i<=m;i++)
			ans=max(ans,dp[n][i]);
		printf("%d\n",ans);
		if(t)
			printf("\n");
	}
	return 0;
}




经典的做法:
#include<stdio.h>
#include<string.h>
#define N 505
int dp[N];/*b字符串到第j个字符时,该字符位置处的公共递增因子数*/
int a[N],b[N];
int max(int a,int b)
{
	return a>b?a:b;
}
int  main()
{
	int t,i,j,k,n,m,ans;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		scanf("%d",&m);
		for(i=1;i<=m;i++)
			scanf("%d",&b[i]);
		memset(dp,0,sizeof(dp));
		dp[0]=-1;
		ans=0;
		for(i=1;i<=n;i++)
		{
			k=0;
			for(j=1;j<=m;j++)
			{
/*当前要比较的数值为a[i],所以我们寻找b[j]中比a[i]小,但dp[j]最大的值,找到了就用k记录位置*/ 
				if(a[i]>b[j]&&dp[j]>dp[k])
					k=j;
				if(a[i]==b[j])
					dp[j]=(dp[k]>=0?dp[k]:0)+1;/*dp[k]值+1就为当前字符的公共递增因子数*/
			}
		}
		for(i=1;i<=m;i++)
			ans=max(ans,dp[i]);
		printf("%d\n",ans);
		if(t)
			printf("\n");
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值