首页 > 代码库 > 尝试解释LIS(最长递增子序列)的一种动态规划算法

尝试解释LIS(最长递增子序列)的一种动态规划算法

最长上升子序列就是求给定序列的最长的递增序列,其中不要求序列的元素在原序列中保持连续。

为了方便理解,可以举个例子:

inta[] = {0,2,1,5,3,6,4,8,9,7}(数组下标从1开始)的一个最长的子序列1,3,4,7,9。

利用动态规划的思想,可以方便的求取这个解。

为了方便解释,我们定义dp(n)为长度为1至下标为n的最长子序列的长度(数组下标假设从1开始),{a[1],a[2],..,a[n]}为dp(n)对应的序列。

为了和程序对应,我采取自底向上的方式进行解释。

1.显然对于序列dp(1) 对应的{2}来说,最长递增子序列的长度就是1;

2.对于序列dp(2) 对应的{2,1},我们首先比较a[2]和a[1]大小,

若a[2]>a[1],显然此时的{a[1],a[2]}是一个最长子序列,故dp(2) = dp(1)+1=2,

若a[2]<=a[1], 此时一个最长子序列的长度仍然为1,即dp(2) = 1;

在我们的例子中a[2]= 1 <a[1]=2,故dp(2)=1;

 

3.那么对于包含三个数的序列dp(3)对应的{2,1,5}来说呢

此时a[3]要和a[1], a[2]进行比较,此时可能会出现这种情况

a[3]>a[1],由于子序列可以是不连续的,故{a[1],a[3]}可以是一个子序列,dp[3]=dp[1]+1

a[3]>a[2],说明以a[2]为末端的子序列(可以不连续哦)+a[3]能形成一个新的子序列,此时要给dp[3]赋值,

那么dp[3]是等于dp[1]+1还是dp[2]+1呢

既然我们要求最长子序列,那么就要让dp[3]=max{dp[2],dp[1]}+1

若a[3]<a[1],a[3]<a[2]

还是令dp[3] = 1;

例子中a[3]>a[1],a[3]>a[2], dp[3]=max{dp[2],dp[1]}+1= 2

…………………………………………………

这样问题就就转化为在dp[1],dp[2],…,dp[n-1]的情况下如何求取dp[n]

若a[n]>a[i],a[n]>a[j],a[n]>a[k]….,(i,j,k…属于1至n-1)

dp[n]=max{dp[i],dp[j],dp[k],,,,,,}+1

若不满足

dp[n]=1

// DoubleLIS.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "stdio.h"
#define N 10
int dp[N];//表示从A[1]到A[i]中以A[i]结尾的最长子序列长度
int a[N] = {0,2,1,5,3,6,4,8,9,7};

int LIS(int n)  //从左往右最长序列的长度
{
	int ans = 1;
	for (int i = 2; i < N; i++)
	{
		int tmp = i;
		int max = 0;
		for (int j = 1; j < i; j++)
		{
			//从左向右搜索dp数组,若满足上升序列的条件
			//1.若a[i] > a[j]
			//找到dp数组中最大的值,即A[i]之前最大的值
			//2.若a[i]不满足a[i] > a[j]
			//则dp[i] = 1
			if (a[i] > a[j] && dp[j] > max)
			{
				tmp = j;
				max = dp[j];
			}
		}

		//最大值+1就是dp[i]
		dp[i] = dp[tmp] + 1;

		if (dp[i] > ans)
			ans = dp[i];
	}
	
	return ans;

}
int main(int argc, char* argv[])
{
	dp[1] = 1;
	printf("%d\n", LIS(N-1));  //从左往右最长序列的长度
	return 0;
}