zoukankan      html  css  js  c++  java
  • LIS——续

    LIS_续

    LIS的二分做法

    首先来看看一个序列 1 3 5 4 6 8 2

    按照之前的滑动窗口的处理思路,你在一个元素入队之前判断他和队顶元素的大小譬如队列是1 3 现在你来一个2,你会发现把2替换3,这样的排列1 2可以给后面的元素提供更大的入度

    你单独只放1 3后面假如再来个3就无法入队了,但是1 2的话3就可以进去,这样的话此时的LIS就是最优的,每次我们都从已入队元素中找到第一个大于等于他的元素,然后用当前元素替换他。如果他比队尾元素还大就直接放入队尾

    但是要注意 1 3 5 4 6 8 2,假设B数组是储存LIS最小优的数组,len就是LIS

    遇到1, B={1} len=1

    遇到3, B={1,3} len=2

    遇到5, B={1,3,5} len=3

    遇到4, B={1,3,4} len=3

    遇到6, B={1,3,4,6} len=4

    遇到8, B={1,3,4,6,8} len=5

    遇到2, B={1,2,4,6,8} len=5可以发现此时的B不是真正的LIS序列,他只是保证单项入度最优,并不影响LIS


    luoguP1020

    第一问,导弹高度不大于之前的导弹高度,所以就是一个非上升连续子序列,也就是a[i]>=a[j] (i>j)

    第二问,一套导弹只能拦截他之后高度比它小的,也就意味着后面比他高的就无法拦截,仔细想想就是求LIS

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=4e5+7;
    #define  ll long long
    int  a[maxn];
    int  dp_1[maxn];///LIS
    int  dp_2[maxn];///最长非上升子序列
    int n=0;
    int main()
    {
        int x;
        while(scanf("%d",&x)!=EOF)
        {
            a[++n]=x;
        }
        int ans_1=0;
        int ans_2=0;
        memset(dp_1,0,sizeof (dp_1));
        memset(dp_2,0,sizeof (dp_2));
        for (int i=1;i<=n;++i)
        {
            dp_1[i]=1;
            for (int j=1;j<i;++j)///找寻之前的是否可以合并
            {
                if (a[i]>a[j])
                {
                    ans_1 = max(ans_1,dp_1[i]=max(dp_1[i],dp_1[j]+1));
                }
                
            }
            ans_1=max(ans_1,dp_1[i]);///判断是否加入原来的子序列,否则直接以a[i]单独创建一个子序列
        }
        for (int i=n;i>=1;--i)///仔细理解后就会发现就是反向跑一遍lis
        {
            dp_2[i]=1;
            for (int j=i+1;j<=n;++j)
            {
               if (a[i]>=a[j])///记得加上等于
                {
                   ans_2 = max(ans_2,dp_2[i]=max(dp_2[i],dp_2[j]+1));
                }
            }
    
            ans_2=max(ans_2,dp_2[i]);
        }
        printf("%d
    %d
    ",ans_2,ans_1);
        return 0;
    }
    

    O(n^2)的复杂度过了一半

    看看二分的

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=4e5+7;
    const int INF=0x3f3f3f3f;
    #define ll long long
    int B[maxn];///入队LIS数组
    int A[maxn];///原数组
    int C[maxn];///最长非上升子序列数组
    int n=0;
    int A_search(int r,int x){///>
        int mid,l=1;
        while (l<=r){
           mid=(l-r)/2+r;
           if (B[mid]<x)
           {
               l=mid+1;
           }
           else
           {
               r=mid-1;
           }
        }
        return l;///最后满足就是刚好小于
    }
    
    int B_search(int r,int x){///>=
        int mid,l=1;
        while (l<=r){
           mid=(l-r)/2+r;
           if (C[mid]<=x)
           {
                l=mid+1;
           }
           else
           {
                r=mid-1;
           }
        }
        return l;
    }
    
    int main()
    {
        while(EOF!=scanf("%d",&A[++n])){
        }--n;
        int lis=1;
        B[1]=A[1];
        for (int i=2;i<=n;++i){
    
            if (A[i]>B[lis]){///如果当前比入队的最大值还大就直接入队
                B[++lis]=A[i];
          }
            else {
                 int t= A_search(lis,A[i]);///找到第一个大于等于A[i]的元素位置
                 B[t]=A[i];///替换
          }
        }
        int ans=1;
        C[1]=A[n];///倒序
        for (int i=n-1;i>=1;--i){///反向查询
              if (A[i]>=C[ans]){///如果当前比入队的最大值还大或者等于就直接入队
                C[++ans]=A[i];
    
          }
            else {///由于等于的也可以入队,所以查找时直接就查找大于的位置
                 int t= B_search(ans,A[i]);///找到第一个大于A[i]的元素位置
                 C[t]=A[i];///替换
                // cout<<t<<endl;
          }
        }
        printf("%d
    %d
    ",ans,lis);
        return 0;
    }
    
    

    当然你也会发现直接找位置就不必判断了,所以选择lowerbound和upperbound

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=4e5+7;
    const int INF=0x3f3f3f3f;
    #define  ll long long
    int n=0;
    int a[maxn];///储存原数组
    int b[maxn];///储存长度
    int c[maxn];///储存当前最小子数组LIS
    int d[maxn];///储存长度
    int f[maxn];///储存当前最小子数组,非上升子序列
    int main()
    {
         while (scanf("%d",&a[n])!=EOF)
        {
            ++n;
        }
        fill(c,c+n,INF);
        fill(d,d+n,INF);///由于是要直接得到位置,则最后超过实际的查询长度遇到INF就截止了
        int maxx_1=-INF;
        for (int i=0;i<n;++i)
        {
            int j=lower_bound(c,c+n,a[i])-c;///减去本身得到位置
            b[i]=j+1;
            maxx_1=max(b[i],maxx_1);
            c[j]=a[i];///直接取代
        }
        int maxx_2=-INF;
        for (int i=n-1;i>=0;--i)
        {
            int j=upper_bound(d,d+n,a[i])-d;///由于等于号也算进去,此时只要找到最大位置即可
            f[i]=j+1;
            maxx_2=max(f[i],maxx_2);
            d[j]=a[i];
        }
        printf("%d
    %d
    ",maxx_2,maxx_1);
        return 0;
    }
    
    齐芒行,川锋明!
  • 相关阅读:
    fedora上部署ASP.NET——(卡带式电脑跑.NET WEB服务器)
    SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志
    8086CPU的出栈(pop)和入栈(push) 都是以字为单位进行的
    FTP 服务搭建后不能访问问题解决
    指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
    Linux 安装MongoDB 并设置防火墙,使用远程客户端访问
    svn Please execute the 'Cleanup' command. 问题解决
    .net 操作MongoDB 基础
    oracle 使用绑定变量极大的提升性能
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。
  • 原文地址:https://www.cnblogs.com/qimang-311/p/13396449.html
Copyright © 2011-2022 走看看