• 最长下降序列——中高级


    Description

    给你一串整数,从左到右按顺序挑出一些数字,构成严格下降序列,问最长能构成多长
    如6个数4 5 2 4 2 2, 那么最长可以挑出5 4 2,长度为3

    Input

    首先输出T(T<=50),表示有T组数据。 
    每组数据: 
    首先输入n表示有几个数(1<n<10000)。 
    后面一行跟着n个整数

    Output

    对于每个样例输出一个数字,表示最长的长度

    Sample Input

    1 6 4 5 2 4 2 2

    Sample Output

    3
    这题可以当模板用,用了类似单调队列的思想。 模板1:严格下降子序列
    <pre name="code" class="cpp">#include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    #define maxn 100030
    int a[maxn],dp[maxn];
    int findx(int l,int r,int x){
        int i,j,mid;
        while(l<=r){
            mid=(l+r)/2;
            if(dp[mid]==x)return mid;
            if(dp[mid]>x){
                l=mid+1;
            }
            else r=mid-1;
        }
        return l;
    }
    int main()
    {
        int n,m,i,j,len;
        while(scanf("%d",&n)!=EOF)
        {
            for(i=1;i<=n;i++){
                scanf("%d",&a[i]);
            }
            len=1;dp[1]=a[1];
            for(i=2;i<=n;i++){
                if(a[i]<dp[len]){
                    len++;dp[len]=a[i];continue;
                }
                j=findx(1,len,a[i]);
                dp[j]=a[i];
            }
            printf("%d
    ",len);
        }
        return 0;
    }
    


    模板2:非严格下降子序列
    <pre name="code" class="cpp">#include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    #define maxn 100030
    int a[maxn],dp[maxn];
    int findx(int l,int r,int x){
        int i,j,mid;
        while(l<=r){
            mid=(l+r)/2;
            if(dp[mid]>x){
                l=mid+1;
            }
            else r=mid-1;
        }
        return l;
    }
    int main()
    {
        int n,m,i,j,len;
        while(scanf("%d",&n)!=EOF)
        {
            for(i=1;i<=n;i++){
                scanf("%d",&a[i]);
            }
            len=1;dp[1]=a[1];
            for(i=2;i<=n;i++){
                if(a[i]<=dp[len]){
                    len++;dp[len]=a[i];continue;
                }
                j=findx(1,len,a[i]);
                dp[j]=a[i];
            }
            printf("%d
    ",len);
        }
        return 0;
    }
    

    
    模板3:可以用upperbound(非严格上升子序列),lower_bound(严格上升子序列)。
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<math.h>
    #include<vector>
    #include<map>
    #include<set>
    #include<queue>
    #include<stack>
    #include<string>
    #include<algorithm>
    using namespace std;
    #define maxn 100030
    #define ll long long
    #define inf 0x7fffffff
    int a[maxn],dp[maxn];
    int main()
    {
        int n,m,i,j,len;
        while(scanf("%d",&n)!=EOF)
        {
            for(i=1;i<=n;i++){
                scanf("%d",&a[i]);
            }
            len=1;dp[1]=a[1];
            for(i=2;i<=n;i++){
                if(a[i]>dp[len]){     //这里求严格上升和非严格等号有区别
                    len++;dp[len]=a[i];
                    continue;
                }
                j=lower_bound(dp+1,dp+1+len,a[i])-dp;//严格上升
                dp[j]=a[i];
            }
            printf("%d
    ",len);
        }
        return 0;
    }

    模板4:用线段树来查找
    思路:因为dp[i]=max(dp[j])+1,dp[i]表示以i结尾的最大上升子序列,那么我先用结构体记录a[i].idx和a[i].num,分别表示编号为第i个数的编号和大小。然后对这个结构体进行排序,按num的值从小到大排序,idx的值根据是否为严格上升排序。然后我每次循环的时候,只要在线段树中查找编号小于等于a[i].idx的最大子序列长度,然后把查找的值更新到线段树就行了。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #include <string>
    #include <cmath>
    #include <cstdlib>
    #include <ctime>
    #include <stack>
    using namespace std;
    #define maxn 10100
    #define inf 999999999
    int c[maxn];
    struct node1{
        int num,idx;
    }a[maxn];
    bool cmp(node1 a,node1 b){
        if(a.num==b.num)return a.idx>b.idx; //这里如果是非严格上升子序列,那么变为a.idx<b.idx;
        return a.num<b.num;
    }
    struct node{
        int l,r,maxnum;
    }b[4*maxn];
    
    void build(int l,int r,int i)
    {
        int mid;
        b[i].l=l;b[i].r=r;b[i].maxnum=0;
        if(l==r){return;}
        mid=(l+r)/2;
        build(l,mid,i*2);
        build(mid+1,r,i*2+1);
    }
    void update(int idx,int num,int i)
    {
        int mid;
        if(b[i].l==idx && b[i].r==idx){
            b[i].maxnum=num;
            return;
        }
        mid=(b[i].l+b[i].r)/2;
        if(idx<=mid)update(idx,num,i*2);
        else{
            update(idx,num,i*2+1);
        }
        b[i].maxnum=max(b[i*2].maxnum,b[i*2+1].maxnum);
    }
    int question(int l,int r,int i)
    {
        int mid;
        if(b[i].l==l && b[i].r==r){
            return b[i].maxnum;
        }
        mid=(b[i].l+b[i].r)/2;
        if(r<=mid)return question(l,r,i*2);
        else if(l>mid)return question(l,r,i*2+1);
        else{
            return max(question(l,mid,i*2),question(mid+1,r,i*2+1));
        }
    }
    int main()
    {
        int n,m,i,j,T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(i=1;i<=n;i++){
                scanf("%d",&c[i]);
            }
            reverse(c+1,c+1+n);
            for(i=1;i<=n;i++){
                a[i].num=c[i];
                a[i].idx=i;
            }
            sort(a+1,a+1+n,cmp);
            build(1,10050,1);
    
            int maxx=0;
            for(i=1;i<=n;i++){
                int num=question(1,a[i].idx,1);
                num++;
                maxx=max(maxx,num);
                update(a[i].idx,num,1);
            }
            printf("%d
    ",maxx);
        }
        return 0;
    }
    


    
    

    
                
    
  • 相关阅读:
    如何使用Orchard搭建敏捷个人的网站(1)
    英语:敏捷英语学习开始了
    英语:普特三步听写法(转载)
    色拉英语第一集第五幕:好胖的一只鸟
    介绍一个基于ASP.NET MVC的框架Catharsis
    色拉英语第2集第3幕:He’s my favorite
    Orchard:如何生成Hello World模块
    如何使用Orchard搭建敏捷个人的网站(2)
    色拉英语第一集第四幕:我不喜欢北京烤鸭
    色拉英语第一集第二幕:请问南京路怎么走?
  • 原文地址:https://www.cnblogs.com/herumw/p/9464786.html
Copyright © 2020-2023  润新知