最长上升子序列(LIS)

最长上升子序列(LIS)

  • 给定长度为N的序列ai,求序列中一个长度最长且递增的子序列。输出子序列的长度。N1000

O(N2)解法

  • f[i]为以ai结尾的最长上升子序列的长度

    易知f[i]的最大值为 区间[1,i)中的 小于ai的最大的j加上1

    意思就是,

    假设aj为区间[1,i)中小于ai的最大值,那么ai就可以拼在aj的后面,这时以ai结尾的LIS的长度就为以aj结尾的LIS长度+1

    用状态转移方程表示就是

    f[i]=maxj=1i1(f[j])+1,ai>aj

  • 用代码表示就是

    int A[N+1];
    int f[N+1];
    for(int i = 1;i<=N;i++){
    for(int j = 1;j<i;j++){
    if(A[i] <= A[j]) continue;
    f[i] = max(f[i],f[j]+1);
    }
    }
    int max_ = -1;
    for(int i = 1;i<=N;i++) max_ = max(f[i],max_);

O(NlogN)解法

  • f[i]为长度为i的上升子序列的最后一个数字

    aj为序列的第j个元素

    fiaj<fi+1,则将fi的值更新为aj

    相当于将长度为i的上升子序列的最后一位fi改为了更小的aj,显然这样更有利于在后面接其他元素

  • 但是目前遍历长为n的序列,对于其中每项aj,都需要进行至多i次操作去寻找合适的区间,时间复杂度仍然为O(N2)

  • 但是,由于fi单调递增,可以在查找的时候使用二分查找来优化,这样就至多进行logN次查找,时间复杂度降为O(NlogN)

  • 代码实现

    int A[N+1];
    int f[N+1];
    int cnt = -1;
    //cnt代表当前最长上升子序列的长度
    fill(f+1,f+N+1,1e9);
    //fill(st,ed,val)意为用val填充数组[st,ed)区间
    //使用极大值填充区间
    for(int i = 1;i<=N;i++){
    int j = lower_bound(f+1,f+N+1,A[i]) - f;
    //lower_bound(st,ed,val)意为在[st,ed)区间内查找第一个不小于val的元素
    //lower_bound返回一个指向找到元素的迭代器
    //减去的f是地址
    //地址 - 地址 = 下标
    cnt = max(cnt,j);
    f[j] = A[i];
    }
    int ans = cnt;
posted @   Burnling  阅读(90)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示