• 最长不下降子序列 nlogn && 输出序列


    最长不下降子序列实现:

           利用序列的单调性

      对于任意一个单调序列,如 1 2 3 4 5(是单增的),若这时向序列尾部增添一个数 x,我们只会在意 x 和 5 的大小,若 x>5,增添成功,反之则失败。由于普通代码是从头开始比较,而 x 和 1,2,3,4 的大小比较是没有用处的,这种操作只会造成时间的浪费,所以效率极低。对于单调序列,只需要记录每个序列的最后一个数,每增添一个数 x,直接比较 x 和末尾数的大小。只有最后一个数才是有用的,它表示该序列的最大限度值。

      实现方法就是新开一个数组 d,用它来记录每个序列的末尾元素,以求最长不下降为例,d[k] 表示长度为k的不下降子序列的最小末尾元素。

      我们用 len 表示当前凑出的最长序列长度,也就是当前 d 中的最后那个位置。

      这样就很 eas了,每读入一个数 x,如果 x 大于等于 d[len],直接让 d[len+1]=x,然后 len++,相当于把 x 接到了最长的序列后面;

      如果 x 小于 d[len],说明 x 不能接到最长的序列后面,那就找 d[1...len−1] 中末尾数小于等于 x 的的序列,然后把 x 接到它后面。举个例子,若当前 x==7,len==8

    1

    2

    3

    4

    5

    6

    7

    8

    2

    3

    4

    7

    7

    10

    12

    29

      d[1]d[5] 均小于等于 x,若在 d[1] 后接 x,则 d[2] 应换成 x,但 d[2]==3,比 x 小,能接更多的数,用 7 去换 3 显然是不划算的,所以 x 不能接 d[1] 后。同理,d[2]⋯d[4] 均不能接x。由于 d[5]x 且 x<d[6]7 能比 10 接更多的数,所以选择在 d[5] 后接 x,用 x 替换 10。

      根据这个操作过程,易知数组 d 一定是单调的序列,所以查找的时候可以用二分!二分效率是 logn 的,所以整个算法的效率就是 nlogn 的啦~

    输出序列实现:

           想了好久,认为 nlogn 做法也是可以输出序列的,这时候需要增加一个 c 数组 用来记录每个元素在最长序列中的位置,即 c[i] 表示 a[i] 被放到了序列的第几个位置。

      输出时,从 数组 a 的尾部开始,逆序依次找出 c 为 len, len-1, len-2 … 3, 2, 1 的元素,并且找到一个就接着寻找 c[i]-1,直到找到 c[i] 为 1 的数。

      举个例子:

    a: 13 7 9 16 38 24 37 18 44 19 21 22 63 15
    c: 1 1 2 3 4 4 5 4 6 5 6 7 8 3

      len = 8;

      我们从 15 开始倒着找 c 为 8 的元素,找到 63,接着找 c 为 7 的,找到 22,再找 c 为 6 的,找到 21,再找 c 为 5 …… 以此类推。

      从而,我们得出的序列为 63,22,21,19,18,16,9,7

      逆序输出来,就是 7,9,16,18,19,21,22,63

      为什么这个方法是对的呢?倒序查找保证了两个条件:

        - 如果 c 中有多个相同的数,后面的一定是最新更新的;

        - 在保证一条件的前提下,倒序找,后面的数一定可以接到前面数的后面。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<stack>
     6 #define int long long
     7 #define maxn 1000+10
     8 using namespace std;
     9 inline int read() 
    10 {
    11     int x=0;
    12     bool f=1;
    13     char c=getchar();
    14     for(; !isdigit(c); c=getchar()) if(c=='-') f=0;
    15     for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    16     if(f) return x;
    17     return 0-x;
    18 }
    19 inline void write(int x)
    20 {
    21     if(x<0){putchar('-');x=-x;}
    22     if(x>9)write(x/10);
    23     putchar(x%10+'0');
    24 }
    25 int n,len;
    26 int a[maxn],last[maxn],site[maxn];
    27 stack<int> s;
    28 signed main()
    29 {
    30     n=read();
    31     for(int i=1;i<=n;i++) a[i]=read();
    32     if(n==0) 
    33     {
    34         write(0);
    35         return 0;
    36     }
    37     len=1;
    38     last[1]=a[1];
    39     site[1]=1;
    40     for(int i=2;i<=n;i++)
    41     {
    42         if(a[i]>=last[len]) 
    43         {
    44             last[++len]=a[i];
    45             site[i]=len;
    46         }
    47         else 
    48         {
    49             int now=lower_bound(last+1,last+len+1,a[i])-last;
    50             last[now]=a[i];
    51             site[i]=now;
    52         }
    53     }
    54     printf("max=");
    55     write(len);
    56     printf("
    ");
    57     for(int i=n,j=len;i>=1;i--)
    58     {
    59         if(site[i]==j) 
    60         {
    61             s.push(a[i]);
    62             j--;
    63         }
    64         if(j==0) break;
    65     }
    66     while(!s.empty())
    67     {
    68         int put=s.top();
    69         s.pop();
    70         write(put);
    71         printf(" ");
    72     }
    73     return 0;
    74 }

    请各位大佬斧正(反正我不认识斧正是什么意思)

  • 相关阅读:
    前端学习笔记2017.6.12 DIV布局网页
    前端学习笔记2017.6.12 HTML的结构以及xhtml、html、xml的区别
    前端学习笔记2017.6.21-html和浏览器的关系以及开发工具
    前端学习笔记2017.6.21-html是个什么东西
    前端学习笔记2017.6.21-引入JS文件的方法
    学习一下阿里技术文档的写作方法
    PS中图层混合模式的计算方法
    3dsmax里面的喷射器spray和超级喷射器superspray是个什么东西
    案例14-实现注册功能和激活功能
    012-MD5Utils工具类模板
  • 原文地址:https://www.cnblogs.com/handsome-zyc/p/11609456.html
Copyright © 2020-2023  润新知