• 子序列问题【LIS、LCS、LCIS】


    最长上升子序列【LIS】

    Input

    14
    13 7 9 16 38 24 37 18 44 19 21 22 63 15

    Output

    max=8
    7 9 16 18 19 21 22 63


     光求长度很容易,但记录路径就麻烦了(至少我想一下午没想出来)

    解析:

    • 记录长度很容易,从前往后扫一遍,记录每次 1~i 的最大长度
    • 于此同时,另开一个数组结构体,(或一个二维数组)记录路径,为了复制方便,推荐用结构体哦
    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    struct qu{
        int l[101];
    }line[101];
    int a[101],f[101];
    
    int main()
    {
        int n,aim,ans=1;
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
        
        for(int i=1;i<=n;++i)
        {
            f[i]=1;
            line[i].l[1]=a[i];
            for(int j=1;j<i;++j)
            {
                if(a[j]<a[i] && f[j]+1>f[i])
                {
                    f[i]=f[j]+1;
                    line[i]=line[j];
                    line[i].l[f[i]]=a[i];
                }
            }
            if(ans<f[i])
            {
                ans=f[i];
                aim=i;
            }
        }
        printf("max=%d
    ",ans);
        for(int i=1;i<=ans;++i)
            printf("%d ",line[aim].l[i]);
        return 0;
    }

    updated:2018-11-04

    nlogn 做法

    f[i] 记录长度为 i 结尾最小的数,二分查找替换

    #include<stdio.h> 
    #include<algorithm> 
    using namespace std;
    int n,a[1010],f[1010];
    
    int main() 
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i) {
            scanf("%d",&a[i]);
        }
        f[1]=a[1];
        int len=1;
        for(int i=2;i<=n;++i) {
            int l=0,r=len,mid;
            if(a[i]>f[len]) f[++len]=a[i];
            else {
                while(l<r) 
                {
                    mid=(l+r)>>1;
                    if(f[mid]>a[i]) r=mid; //大了 
                    else l=mid+1;
                }
                f[l]=min(a[i],f[l]);
            }
        }
        printf("%d",len);
        return 0;
    }

    -------------------------------------------------------分割线

    最长公共子序列【LCS】 

    Input

    5

    3 2 1 4 5

    1 2 3 4 5

    Output

    3


    回想当年学DP的心路历程(抄代码),现在觉得刚能看懂 DP 就要退役了

    解析:

    最基本的O(n2)做法,f[i][j] 表示A序列匹配到i位,B序列匹配到j位的最长公共子序列

    思路很简单,如果当前匹配:f[i][j]=f[i-1][j-1]+1;

    否则接上之前的答案:f[i][j]=max(f[i-1][j],f[i][j-1]);

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    const int N=10001;
    int a[N],b[N],f[N][N];
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i)    
            scanf("%d",&a[i]);
        for(int i=1;i<=n;++i)
            scanf("%d",&b[i]);
        
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                if(a[i]==b[j]) 
                    f[i][j]=f[i-1][j-1]+1;
                else 
                    f[i][j]=max(f[i-1][j],f[i][j-1]);    
            }
        }
        printf("%d",f[n][n]);
        return 0;
    }

    O(nlogn) 做法需要新建立一个映射关系

    将A序列的值对应的下标映射,然后我们通过扫描B序列的值,可以得到B在A序列中的位置关系,如果上升,则公共

    于是问题转化为了求LIS

    code

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    const int MX=1e5+1;
    int yins[MX],a[MX],b[MX],f[MX];
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i) {
            scanf("%d",&a[i]);
            yins[a[i]]=i;f[i]=0x3f3f3f3f;
        }
        for(int i=1;i<=n;++i) scanf("%d",&b[i]);
        int len=0;
        for(int i=1;i<=n;++i)
        {
            int l=0,r=len;
            if(yins[b[i]]>f[len]) f[++len]=yins[b[i]];
            else {
                while(l<r) {
                    int mid=(l+r)>>1;
                    if(yins[b[i]]<f[mid]) r=mid;
                    else l=mid+1;
                }
                f[l]=min(f[l],yins[b[i]]);
            }
        }
        printf("%d",len);
        return 0;
    }

     

    -------------------------------------------------------分割线

    最长公共上升子序列【LCIS】

    Input

    5
    1 4 2 5 -12
    4
    -12 1 2 4

    Output 

    2


     这是xing哥哥当时布置的作业(一直没做……关键当时不会呀)

    解析:

    O(n3)做法很好理解,A、B中找到相同的元素,往B前扫一遍,找到一个能接的上的最长的……

    更新答案就好了

    code

    #include<stdio.h> 
    #include<algorithm> 
    using namespace std;
    int n,m,a[1010],b[1010],f[1010][1010];
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i) {
            scanf("%d",&a[i]);
        }
        scanf("%d",&m);
        for(int i=1;i<=m;++i) {
            scanf("%d",&b[i]);
        }
        for(int i=1;i<=n;++i) 
        {
            for(int j=1;j<=m;++j) 
            {
                if(a[i]==b[j]) {
                    for(int k=0;k<j;++k) {
                        if(a[i]>b[k] && f[i-1][k]+1>f[i][j]) {
                            f[i][j]=f[i-1][k]+1;
                        }
                    }
                }
                else f[i][j]=f[i-1][j]; 
            }
        }
        printf("%d",f[n][m]);
        return 0;
    }
    /*
    5
    1 4 2 5 -12
    4
    -12 1 2 4
    */

     

  • 相关阅读:
    Nginx--sub_filter模块
    Scala学习5 objext
    Scala学习4 Class
    Scala学习3 Map
    Scala学习2 Array、ArrayBuffer
    Scala学习1 基本数据类型,条件,循环
    flink1.9新特性:维表Join解读
    Blink源码编译
    Flink Maven项目兼容多版本Kafka
    Ant build.xml程序简单说明
  • 原文地址:https://www.cnblogs.com/qseer/p/9429719.html
Copyright © 2020-2023  润新知