最长递增子序列与最长公共子序列

最长递增子序列:

我们不知道最长递增子序列的最后一个元素出现在什么位置上,因此,需要依次比较各个元素的最长递增子序列的大小(如果给定的序列完全逆序的话,那么以任意元素结尾的最长递增子序列长度均为1,故也需要比较全部)。根据动态规划问题的思想,一个大的问题可以拆分小问题去解决,对于每个小问题,也可以拆分成更小的问题(大事化小,小事化了)。
假设序列元素为a[n],dp[n]为以a[n]结尾的最长递增子序列长度,举以下序列为例进行说明:
最长递增子序列与最长公共子序列_第1张图片
对于第5个元素6,把他作为结尾的最长公共子序列的长度dp[5]就应该是:以他前面所有小于他的数 作为结尾的最长公共子序列的最大值。这样才能保证序列是递增的。
因此不难列出状态转移方程:dp[i]=max(dp[i],dp[j]+1)
其中i为第i个元素的下标,j为[1,i-1]区间内的值,通过循环得到这个区间内的最长递增子序列长度。
最终通过比较[1,n]区间内dp[i]的值即可得出最长递增子序列的长度。
代码:

//最长递增子序列
#include
#define max(a,b) (a>b?a:b)
#define MAXN 10005
int main()
{
	int i,j,k,max_len=1,n;
	int a[MAXN],dp[MAXN];
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		dp[i]=1;		//初始最长递增子序列长度是1(元素本身)
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=2;i<=n;i++)
	{
		for(j=1;j<=i-1;j++)
		{
			if(a[j]max_len)
		max_len=dp[i];
	}
	printf("%d",max_len);
	return 0;
}

最长公共子序列:

求最长公共子序列长度的思路如下:假设两个序列分别用a[N],b[M]表示,用dp[i][j]表示以a序列第i个元素结尾,以b序列第j个元素结尾的最长公共子序列的长度,那么对于a[i]和b[j]存在如下情况:
(1)a[i]==b[j] 此时由于两个序列的某位置元素相等,那么此时这组元素一定算在最长公共子序列的长度内,因此长度也就是下标为(i-1)(j-1)时的长度加一,状态转移方程为:
dp[i][j]=dp[i-1][j-1]+1, a[i]==b[j]
(2)a[i]与b[j]不相等,那么此时的dp[i][j] 可能由两种情况的一种推来。dp[i][j]的值当然取这两种情况的最大者。状态转移方程为:
dp[i][j]=max{dp[i-1][j],dp[i][j-1]}

代码:

//最长公共子序列
#include
#include
#define max(a,b) (a>b?a:b)
#define MAXN 105
int main()
{
	int dp[MAXN][MAXN],i,j,k,len1,len2;
	char a[MAXN],b[MAXN];
	scanf("%s %s",a,b);
	memset(dp,0,sizeof(dp));
	len1=strlen(a);
	len2=strlen(b);
	for(i=1;i<=len1;i++)
	{
		for(j=1;j<=len2;j++)
		{
			if(a[i - 1] == b[j - 1])
				dp[i][j]=dp[i-1][j-1]+1;
			else
				dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
		}
	}
	printf("%d",dp[len1][len2]);
	return 0;
}

你可能感兴趣的:(动态规划)