码迷,mamicode.com
首页 > 其他好文 > 详细

2015 UESTC Training for Dynamic Programming N - 导弹拦截(LIS (nlogn))

时间:2015-05-16 06:46:00      阅读:360      评论:0      收藏:0      [点我收藏+]

标签:

N - 导弹拦截

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
 

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都要高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹,同时,司令部想知道拦截下来的导弹的高度。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

Input

第一行是一个整数t,代表case数。 对于每一个case,第一行是一个整数n(1n100000); 第二行是n个非负整数,表示第n枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。数据保证高度不会超过100000.

Output

对于每一个case,第一行输出最多能拦截的导弹数,第二行按来袭顺序输出拦截下来的导弹的高度构成的序列,以一个空格隔开。若有不止一种方法可以拦截最多的导弹,输出字典序最小的。

Sample input and output

Sample InputSample Output
1
5
1 6 3 5 7
4
1 3 5 7

解题思路:

  这道题一看,我的天,这不是NOIP的题吗。。。仔细一看数据范围加大了,而且还多了输出的要求,就是让你以字典序最小的形式输出这个最长上升子序列.

  好了,先来说说有关求LIS的方法吧,LIS顾名思义,就是我们所说的 longest increasement sequence 啦,比如在一个序列中,1 2 3 4 5 1 那么这个序列的LIS就是5了。关于求LIS有两种常用的算法,一个是O(n2)的,一个是O(nlogn)的,两者的区别就在于寻找a[j]的方式不同,一个是暴力找的,一个是用二分来找的。。

 

  先来说说O(n2)的做法吧。

首先,我们对于知道。对于一个序列,如果我想求出他的最长上升子序列的话,我们要做的事情很简单,每次枚举i的位置,然后从i的后面开始往后找使得a[j]>a[i].

在这里,我们定义一个状态dp[i]表示的是a[i]结尾的最长上升子序列的长度了。我们一开始使得所有的dp[i]=1,就是说在没有枚举前,所有的单个子序列都是以自己为LIS的,下来我们只需要一次一次的更新答案就好了。

  然后是O(nlogn)的做法,为了能够在寻找a[j]>a[i]时更加方便和快捷,我们采用了二分的方法。。

先定义一个数组m[x],表示的是长度为x的上升子序列以m[x]作为最小末尾,比如所m[3]=17说的就是一个长度为3的子序列的末尾元素是17,为什么说是最小的末尾呢?后面会讲到。

  由于是求LIS,顾名思义,m[]数组中的每个元素都是上升的,这样一来,如果x<y,那么m[x]<m[y],结合二分和这个性质就能够很快捷的进行各种查找了。

比如说,我们假定m[]存放的目前来说最长上升子序列的长度是k,那么当我们枚举到第i个数字时,如果a[i] > m[k],则m[++k] = a[i];

否则的话,我们就在[1,k]中利用二分去找一个pos,使得pos具有这样的性质,小于等于a[i]的最大位置,m[p] = a[i];

比如说现在已经知道了k=6,m[6]=17,  1 2 3 11 16 17,此时的a[i] = 7的话,因为a[i]=7<m[k],所以m[3] = 7。看懂了吗?就是说,在一个长度为3的上升子序列中,他的最小末尾是7.

 

代码:

2015 UESTC Training for Dynamic Programming N - 导弹拦截(LIS (nlogn))

标签:

原文地址:http://www.cnblogs.com/wikioibai/p/4507276.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!