O(NlogN)做法:贪心+二分 (该想法是在O(n^2)的动态规划中进一步演化而来)
p[i]表示第i个元素,
dp[i]表示长度为i+1的LIS结尾元素的最小值
利用贪心的思想,对于一个上升子序列,最后面的一个元素越小,越有利于添加新的元素,这样LIS长度更长,
所以我们要维护dp数组,其表示的就是长度为i+1的LIS结尾元素的最小值,保证每一位都是最小值,
这样dp数组的长度就是LIS的长度,即答案。
给定数组{1,7,3,5},求最长上升子序列的长度,
(1)dp【0】=p【0】=1,长度为1的LIS,就为第一个数
(2)dp【1】=p【1】=7,因为p【1】>dp【0】,直接添加到dp尾
(3)dp【1】=p【2】=3,因为p【2】>dp【1】,所以在LIS中找第一个大于p【2】的元素,并将其替换为p【2】
(4)dp【2】=p【3】=5,因为p【3】>dp【1】,直接添加到dp尾
dp数组维护完毕后,dp数组的长度就是LIS长度==3
在上面第2部中查找第一个大于key的数,采用二分查找更省时
#include<bits/stdc++.h>
using namespace std;
const int maxn=50004;
int p[maxn];
int dp[maxn];
int main()
{
int n,i,j;
scanf("%d",&n);
for(i=0; i<n; ++i)
{
scanf("%d",&p[i]);
}
dp[0]=p[0];
for(i=1,j=0; i<n; ++i)
{
if(p[i]>dp[j])
{
dp[++j]=p[i];
}
else
{
dp[upper_bound(dp,dp+j,p[i])-dp]=p[i]; //第一个大于的
}
}
cout<<j+1<<endl;
return 0;
}