最长上升子序列(LIS)长度 O(nlogn)算法 hdu1950为例

版权声明:编写不易,转载请注明出处,谢谢。 https://blog.csdn.net/WilliamSun0122/article/details/77937812

最长上升子序列

最长上升子序列(Longest Increasing Subsequence,LIS),是指一个序列中最长的单调递增的子序列。
该问题有一个 n2 的动态规划解法,这里介绍 O(nlogn) 的解法。

设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[]是单调递增的,所以我们二分找,这样最后总的时间复杂度就是 O(nlogn)

这个写法有个缺陷,就是不太好得出序列,只能得出序列长度。
比如序列1 20 8 9 7
最长上升子序列应该是1 8 9
但是d[]保存的是1 7 9
动态规划的 n2 解法比较好得出序列,但是感觉时间复杂度又有点高,如果你知道怎么在 O(nlogn) 的时间内(或更短)得出序列(注意不是序列长度),请留言指点我,谢谢。

hdu1950

题意
最开始一个数t表示t组样例,之后每组样例给一个数n(<=40000),之后给出n个数,求最长上升子序列的长度。

题解
裸题,直接写。一般有两种写法,可以自己写二分,可以用STL的lower_bound,下面用第二种写法。

hdu6197 第一种写法

#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;
}

猜你喜欢

转载自blog.csdn.net/WilliamSun0122/article/details/77937812