最长不下降子序列 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 中有多个相同的数,后面的一定是最新更新的;
- 在保证一条件的前提下,倒序找,后面的数一定可以接到前面数的后面。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define int long long
#define maxn 1000+10
using namespace std;
inline int read()
{
int x=;
bool f=;
char c=getchar();
for(; !isdigit(c); c=getchar()) if(c=='-') f=;
for(; isdigit(c); c=getchar()) x=(x<<)+(x<<)+c-'';
if(f) return x;
return -x;
}
inline void write(int x)
{
if(x<){putchar('-');x=-x;}
if(x>)write(x/);
putchar(x%+'');
}
int n,len;
int a[maxn],last[maxn],site[maxn];
stack<int> s;
signed main()
{
n=read();
for(int i=;i<=n;i++) a[i]=read();
if(n==)
{
write();
return ;
}
len=;
last[]=a[];
site[]=;
for(int i=;i<=n;i++)
{
if(a[i]>=last[len])
{
last[++len]=a[i];
site[i]=len;
}
else
{
int now=lower_bound(last+,last+len+,a[i])-last;
last[now]=a[i];
site[i]=now;
}
}
printf("max=");
write(len);
printf("\n");
for(int i=n,j=len;i>=;i--)
{
if(site[i]==j)
{
s.push(a[i]);
j--;
}
if(j==) break;
}
while(!s.empty())
{
int put=s.top();
s.pop();
write(put);
printf(" ");
}
return ;
}
请各位大佬斧正(反正我不认识斧正是什么意思)
最长不下降子序列 nlogn && 输出序列的更多相关文章
- 最长不下降子序列nlogn算法详解
今天花了很长时间终于弄懂了这个算法……毕竟找一个好的讲解真的太难了,所以励志我要自己写一个好的讲解QAQ 这篇文章是在懂了这个问题n^2解决方案的基础上学习. 解决的问题:给定一个序列,求最长不下降子 ...
- hdu1025 最长不下降子序列nlogn算法
C - DP Crawling in process... Crawling failed Time Limit:1000MS Memory Limit:32768KB 64bit I ...
- 最长不下降子序列nlogn
b[i]表示长度为i的最长不下降子序列的最小末尾元素的值显然它是单调递增的,满足二分性质,然后就可以愉快地二分啦. #include<iostream> #include<cstdi ...
- 最长不下降子序列(线段树优化dp)
最长不下降子序列 题目大意: 给定一个长度为 N 的整数序列:A\(_{1}\),A\(_{2}\),⋅⋅⋅,A\(_{N}\). 现在你有一次机会,将其中连续的 K 个数修改成任意一个相同值. 请你 ...
- tyvj 1049 最长不下降子序列 n^2/nlogn
P1049 最长不下降子序列 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 求最长不下降子序列的长度 输入格式 第一行为n,表示n个数第二行n个数 输出格式 ...
- 最长不下降子序列的O(n^2)算法和O(nlogn)算法
一.简单的O(n^2)的算法 很容易想到用动态规划做.设lis[]用于保存第1~i元素元素中最长不下降序列的长度,则lis[i]=max(lis[j])+1,且num[i]>num[j],i&g ...
- 最长不下降子序列//序列dp
最长不下降子序列 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 求最长不下降子序列的长度 输入格式 第一行为n,表示n个数第二行n个数 输出格式 最长不下降 ...
- P1020 导弹拦截(nlogn求最长不下降子序列)
题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...
- hdu 1160 FatMouse's Speed(最长不下降子序列+输出路径)
题意: FatMouse believes that the fatter a mouse is, the faster it runs. To disprove this, you want to ...
随机推荐
- ffmpeg 速查手册
ref : http://linux.51yip.com/search/ffmpeg ffmpeg是一个源于Linux的工具软件,是FLV视频转换器,可以轻易地实现FLV向其它格式avi.asf. m ...
- Microsoft Visual Studio常用快捷键
快速补全关键字 1)tab; 删除整行代码 1)Ctrl + L; 回到上一个光标位置/前进到下一个光标位置 1)回到上一个光标位置:使用组合键“Ctrl + -”; 2)前进到下一个光标位置:“Ct ...
- js 移动端之监听软键盘弹出收起
js 移动端关于页面布局,如果底部有position:fixed的盒子,又有input,当软键盘弹出收起都会影响页面布局.这时候Android可以监听resize事件,代码如下,而ios没有相关事件. ...
- NameError: name “ ” is not defined
NameError: name “ ” is not defined 问题一:name ‘name’ is not defined "name"两端是双下划线"_&quo ...
- canvas炫酷时钟
canvas炫酷时钟 实现的功能 主要用到canvas的一些基础api 直接看效果 html: <canvas id="myCanvas" width="500&q ...
- live555的使用(转载)
Live555 是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了对标准流媒体传输协议如RTP/RTCP.RTSP.SIP等的支持.Live555实现 了对多种音视频编码格式的音视频数据的流 ...
- 用户在浏览器输入URL回车之后,浏览器都做了什么
在直接列出执行的步骤之前先来普及几个知识,相信了解完这些知识之后会对前后端的交互有更深入的理解. 1.TCP连接 TCP:Transmission Control Protocol, 传输控制协议,是 ...
- python 笔记二
17.进程线程 进程间通信方式:管道Pipe:队列Queue:共享内存Value.Array.Manager: 多进程同步:锁Lock.递归锁RLock.Condition(条件变量):事件event ...
- Java中重载(overloading)和重写(Overriding)的区别
一:方法的重载 (1)方法重载指在类中定义方法名相同,参数不同的不同的多个方法(返回值类型可随意,不能以返回类型作为重载函数的区分标准). 参数不同表现: 1.参数的个数不同 2.参数的类型不同 3. ...
- 阿里云SOP
阿里云SOP 摘要 注册阿里云账号. 领取及配置ECS. 领取及配置RDS. 部署网站. 注册阿里云账号 在主页点击注册 填入相应的信息 领取及配置ECS 注册后领取免费的ECS,RDS. 打开控制台 ...