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

最长不下降子序列实现:

利用序列的单调性

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

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

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

  这样就很 easy 了,每读入一个数 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("\n");
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 }

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

原文地址:https://www.cnblogs.com/handsome-zyc/p/11609456.html

时间: 2024-10-24 12:32:01

最长不下降子序列 nlogn && 输出序列的相关文章

最长不下降子序列nlogn算法详解

今天花了很长时间终于弄懂了这个算法……毕竟找一个好的讲解真的太难了,所以励志我要自己写一个好的讲解QAQ 这篇文章是在懂了这个问题n^2解决方案的基础上学习. 解决的问题:给定一个序列,求最长不下降子序列的长度(nlogn的算法没法求出具体的序列是什么) 定义:a[1..n]为原始序列,d[k]表示长度为k的不下降子序列末尾元素的最小值,len表示当前已知的最长子序列的长度. 初始化:d[1]=a[1]; len=1; (0个元素的时候特判一下) 现在我们已知最长的不下降子序列长度为1,末尾元素

toj 4071 最长不下降子序列nlogn解法

题目描述:给出2D空间中的n只鸟的坐标,射手对其进行射击,要求射击的鸟的坐标越来越大,即对于第i和第i+1只鸟,要求满足:xi<=xi+1 && yi <= yi+1.求最多能射击多少只鸟. 思路:将所有点按照x坐标排序,x坐标相同则按照y坐标排序.则x方向上可以满足限制,对y方向上求最长不下降子序列即可.由于数据量较大,需要采取nlogn的优化算法. 1 #include <algorithm> 2 #include <iostream> 3 #inc

tyvj 1049 最长不下降子序列 n^2/nlogn

P1049 最长不下降子序列 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 求最长不下降子序列的长度 输入格式 第一行为n,表示n个数第二行n个数 输出格式 最长不下降子序列的长度 测试样例1 输入 3 1 2 3 输出 3 备注 N小于5000for each num <=maxint 题意:中文题意 题解:不下降也就是>= n^n  dp[i] 表示前i个数的最长不下降子序列的长度 1 /*****************************

hdu 1160 FatMouse&#39;s Speed(最长不下降子序列+输出路径)

题意: FatMouse believes that the fatter a mouse is, the faster it runs. To disprove this, you want to take the data on a collection of mice and put as large a subset of this data as possible into a sequence so that the weights are increasing, but the s

最长不下降子序列//序列dp

最长不下降子序列 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 求最长不下降子序列的长度 输入格式 第一行为n,表示n个数第二行n个数 输出格式 最长不下降子序列的长度 测试样例1 输入 3 1 2 3 输出 3 备注 N小于5000for each num <=maxint 由N小于5000可知可以使用蛋疼的平方算法. 那么首先,我们都知道对于一个数列来讲,不下降子序列最短的的长度肯定是1. 那么我们设置一个f[i],表示以第i个数为结尾的最长不下降

最长不下降子序列 O(nlogn)

1 #include<stdio.h> 2 int a[1000] , temp[1000] ; 3 int n , top ; 4 5 int binary_search (int x) { 6 int fir = 0 ; 7 int last = top ; 8 int mid ; 9 while (fir <= last ) { 10 mid = (fir + last) / 2 ; 11 if ( x <= temp[mid] ) { 12 last = mid - 1;

网络流24题之最长不下降子序列问题

P2766 最长不下降子序列问题 题目描述 ?问题描述: 给定正整数序列x1,...,xn . (1)计算其最长不下降子序列的长度s. (2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列. (3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列. ?编程任务: 设计有效算法完成(1)(2)(3)提出的计算任务. n<=500 输入输出格式 输入格式: 第1 行有1个正整数n,表示给定序列的长度.接下来的1 行有n个正整数n:x1, ...

【模板】最长不下降子序列

====接力dalao完成==== 前文链接:(CSP-S RP++!) 对前文的一些补充: 首先清楚最长不下降子序列是一个递增但是允许不同位元素相等的序列.而最长上升子序列则是一个单调递增的序列. 而两者都是子序列,所以子序列的长度一定小于等于原序列.且子序列在原序列的位置不一定连续. 这个O(nlogn)的算法使用的是贪心的思想. 为了帮助理解,请与以下代码对比阅读: #include<iostream> using namespace std; int a[1000001],dp[100

最长不下降子序列的长度

试题描述 求最长不下降子序列的长度. 设有由n个不相同的整数组成的数列,记为:a[1].a[2].…….a[n].例如:3,18,7,14,10,12,23,41,16,24.若存在0<i1<i2<i3< … < ie 且有a[i1]<=a[i2]<= … <=a[ie]则称为长度为e的不下降序列.如上例中3,18,23,24就是一个长度为4的不下降序列,同时也有3,7,10,12,16,24长度为6的不下降序列. 输入 第一行为n,表示序列中整数的个数,第