zoukankan      html  css  js  c++  java
  • 浅谈最长上升子序列(LIS)

    一、瞎扯的内容

    给一个长度为n的序列,求它的最长上升子序列(LIS)

    简单的dp

    n=read();
    for(i=1;i<=n;i++) a[i]=read();
    for(i=1;i<=n;i++)
        for(j=1;j<i;j++)
            if(a[j]<a[i])
                   dp[i]=max(dp[i],dp[j]+1);
    printf("%d",dp[n]);

    然后发现

    看来需要一个nlogn求LIS的算法

    二、不瞎扯的内容

    上一个算法慢在哪里呢?内层的循环

    如果把它变成二分查找不就是nlogn的算法了吗

    为此需要进行一下改动

    dp数组改为存储长度为i的上升子序列中最小的末尾数字

    这样每当外层循环到了一个新的数字

    a[i]>dp[len]//新的数字比当前LIS的末尾数字大

    dp[++len]=a[i]//愉快地把这个数字接到后面

    如果它小于等于dp[len]?

    举个例子

    序列2 3 1

    i=1:找到2,当前LIS={2},没毛病;

    i=2:找到3,发现它比LIS的最后一个数字大,把它接到后面,当前LIS={2,3};

    i=3:找到1,发现它比3小,那么在当前的LIS中找到一个最小的比1大的数,也就是2,并把2替换成1,当前LIS={1,3}

    Q1:为什么可以这么做呢?

    我们现在把2替换成了1,如果接下来还能把3替换,我们就可以得到一个更优的LIS

    为什么更优?首先LIS是保证尽可能长的,在此基础上,末尾数字越小越优

    Q2:如何在当前的LIS中找到一个最小的比a[i]大的数?

    根据“长度一定时,末尾数字越小结果越优”原则,dp数组一定是递增的

    这个递增就不做证明了,应该比较好想

    所以就可以用二分查找了

    问题解决

    for(i=1;i<=n;i++) dp[i]=0x7fffffff;
    dp[1]=a[1];
        int len=1,l,r,mid;
        for(i=2;i<=n;i++)
        {
            if(a[i]>dp[len]) dp[++len]=a[i];
            else
            {
                l=0;r=len;
                while(l<=r)
                {
                    mid=(l+r)>>1;
                    if(dp[mid]>a[i]) r=mid-1;
                    else l=mid+1;
                }
                dp[l]=min(dp[l],a[i]);
            }
            
        }

    三、来道题

    洛谷P1439

    题目简述:给出两个1~n的序列,求他们的最长公共子序列(n≤100000)

    这跟LIS有什么关系呢?

    还真有关系

    不妨改为在第二个序列中匹配第一个序列,最多匹配多少

    由于两个序列都是1~n的,可将第一个序列重新定义一下

    比如 3 2 1 4 5

    3----1;2----2;1----3;4----4;5----5(对应到它是第几个)

    那么第二个序列 1 2 3 4 5就会变成3 2 1 4 5

    这时再求最长公共子序列的答案是一样的

    然而可以发现第一个序列已经变成了1~n的升序排列,那么第二个序列的子序列 也是第一个序列的子序列的充要条件 是 它是一个上升的序列

    那么问题就变成了求第二个序列的LIS

    用上述方法可以解决

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    inline int read()
    {
        int f=1,x=0;
        char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
        while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int n;
    int a[100005],b[100005],f[100005],g[100005];
    
    int main()
    {
        int i;
        n=read();
        for(i=1;i<=n;i++)
        {
            a[i]=read();
            g[a[i]]=i;
        }
        for(i=1;i<=n;i++)
        {
            b[i]=read();
            b[i]=g[b[i]];
            f[i]=0x7fffffff;
        }
        f[1]=b[1];
        int len=1,l,r,mid;
        for(i=2;i<=n;i++)
        {
            if(b[i]>f[len]) f[++len]=b[i];
            else
            {
                l=0;r=len;
                while(l<=r)
                {
                    mid=(l+r)>>1;
                    if(f[mid]>b[i]) r=mid-1;
                    else l=mid+1;
                }
                f[l]=min(f[l],b[i]);
            }
        }
        printf("%d
    ",len);
        return 0;
    }

    ~祝大家学习信息学顺利~

    BJOI 加油!

  • 相关阅读:
    A Node Influence Based Label Propagation Algorithm for Community detection in networks 文章算法实现的疑问
    Fast Newman-FN算法以及模块度定义介绍
    Label Propagation Algorithm LPA 标签传播算法解析及matlab代码实现
    设计一个smartnic
    Intel GEN11 GPU
    Intel GEN9 GPU
    Shared Virtual Memory (SVM) Functions
    connect via ssh to virtualbox guest vm without knowing ip address
    smartnic
    技术精品翻译
  • 原文地址:https://www.cnblogs.com/llllllpppppp/p/10655838.html
Copyright © 2011-2022 走看看