最长上升子序列(LIS)的 普通解法 和 二分解法

状态转移方程:

dp [ i ]  = max { 1 , dp[ j ] + 1  }     ( j < i   && a[ j ] < a[ i ]   )

先来介绍普通解法:复杂度O(n^2)

题目: 输入n个数,求出其中的最长上升子序列。

#include<iostream>
using namespace std;

int main()
{
	int a[100];//输入数字 
	int dp[100];//用于储存以a[i]结尾的最长上升子序列的 长度 
	int n,m,j,k,i,T;
	cin>>n;
	for (i=0;i<n;i++)
	{
		cin>>a[i];
		dp[i] = 1;//一开始,所有的长度都为 1 
	}
	int ans = -1;//记录d[i]中最大的数,也就是答案 
	for (i=0;i<n;i++)
	{
		for (j=0;j<i;j++)
		{
			if (a[i]>a[j] && dp[j]+1 > dp[i])//对每一个a[i]遍历它之前的,如果可以把 
			{								//这个a[i]插在后面,还能让dp[i]增大,那么就加上去 
				dp[i] = dp[j] + 1;
			}
		}
		ans = max(ans,dp[i]);//记录最大的ans 
	}
	cout<<ans<<endl;
	return 0;
}

 再来介绍二分算法:复杂度O(n*logn)

这里介绍下  STL 的 upper_bound( ) 函数的用法:

upper_bound( a,a+n ,x) : 在数组 a 中 找到 第一个  大于 x 的 数字的 指针

那么 upper_bound( a,a+n ,x) - a   就表示 第一个  大于 x 的 数字的   数组的下标

注意: 数组 a 赋值时要从 下标 0 开始,才能使用这个函数。

思路:

用 lis 数组储存 最长上升子序列的 序列 的最优情况 。

在大于lis [ len ]时 len++,在小于lis[ len ]时可以将arr[ i ]在 lis 中的数进行替换掉。所以此算法主要是在不停的找最合适的起点和最合适的终点。

举例:

输入数组:2 1 5 3 6 4 8 9 7  显而易见 答案为:5

以下是 lis 数组的变化情况 :

第一步:2                   替换

第二步:1                   替换

第三步:1 5                加入

第四步:1 3                替换

第五步:1 3 6             加入

第六步:1 3 4             替换

第七步:1 3 4 8           加入

第八步:1 3 4 8 9        加入

 第九步:1 3 4 7 9       替换

#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
	int a[10000];
	int lis[10000];
	int n,m,j,k,i,T;
	cin>>n;
	
	for (i=0;i<n;i++)
	cin>>a[i];
	
	int len = 0;
	lis[0] = a[0];
	for (i=1;i<n;i++)
	{
		if (a[i] > lis[len])
		lis[++len] = a[i];
		else
		{
			int pos = upper_bound(lis,lis+len,a[i]) - lis;
			lis[pos] = a[i];
		}
	}
	cout<<len+1<<endl;.//最后答案 +1  
	return 0;
} 
 
 
 
 
原文地址:https://www.cnblogs.com/Romantic-Chopin/p/12451160.html