版权声明:编写不易,转载请注明出处,谢谢。 https://blog.csdn.net/WilliamSun0122/article/details/77937812
最长上升子序列
最长上升子序列(Longest Increasing Subsequence,LIS),是指一个序列中最长的单调递增的子序列。
该问题有一个
设a[]是原序列,d[i]表示长度为i的上升子序列的最末元素,若有多个长度为i的上升子序列,则记录最小的那个最末元素。那么d[]肯定是单调递增的(后面会用到这个性质)。
开始我们先令len=1,d[1]=a[1],之后遍历a[],如果d[len] < a[i]那么令d[++len]=a[i],否则就在d[]中找到d[k-1]<a[i]<d[k],然后更新d[k]=a[i]即可,这样到最后len就是最长上升子序列的长度。因为d[]是单调递增的,所以我们二分找,这样最后总的时间复杂度就是
这个写法有个缺陷,就是不太好得出序列,只能得出序列长度。
比如序列1 20 8 9 7
最长上升子序列应该是1 8 9
但是d[]保存的是1 7 9
动态规划的
hdu1950
题意
最开始一个数t表示t组样例,之后每组样例给一个数n(<=40000),之后给出n个数,求最长上升子序列的长度。
题解
裸题,直接写。一般有两种写法,可以自己写二分,可以用STL的lower_bound,下面用第二种写法。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e4+5;
int a[maxn],n;
int d[maxn],len;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
len=1,d[1]=a[1],d[0]=0;
for(int i=2;i<=n;i++)
{
if(d[len]<a[i]) d[++len]=a[i];
else
{
int tmp = lower_bound(d,d+len,a[i])-d;
d[tmp] = a[i];
}
}
printf("%d\n",len);
}
return 0;
}