题目

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是\(\le 50000\)的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式

\(1\)行,若干个整数(个数\(\le 100000\))

输出格式

\(2\)行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

算法分析

我们先看一个例子。

例子

输入数据

10
0 192 100 91 149 146 159 137 17 188

定义三个变量

opt[i]:表示长度为i的不升序列的末位数字最大为opt[i]

opt_n:当前最长不升序列的长度

f[i]:动态规划计算,以第i个导弹结尾的最长不升序列的长度为f[i]

定义一个函数

int s(int i):二分查找最大的k使得opt[k]>=h[i]

初始值

memset(opt,0,sizeof(opt));
opt[1]=h[1];f[1]=1;

状态转移方程

\(f[i]=k+1,k=max\{k\mid opt[k]\ge h[i]\}\)

如果\(\{k\mid opt[k]\ge h[i]\}=\varnothing\)则\(k=0\)

遍历

代码

for(int i=2;i<=n;++i){
k=s(i);f[i]=k+1; //求最大的k使得opt[k]>=h[i],则f[i]=k+1
if(f[i]<=opt_n){ //如果f[i]不超过opt_n,则考虑更新opt[f[i]]
if(opt[f[i]]<h[i]) opt[f[i]]=h[i];
}else opt_n++,opt[opt_n]=h[i];
}

手算理解(建议先看总结,看不懂再看这里):

  1. i=2,h[i]=192,没有1<=k<=n使得opt[k]>192,那么k=s(i)=0f[i]=0+1=1

    此时f[i]>opt_n=0,即 当前方案长度 超过了 opt中已知的最长序列的长度,则需要更新opt(发现了更长的不升序列,补到opt中)。

    opt_n++; //opt_n=1
    opt[opt_n]=h[i] //opt[1]=192
  2. i=3,h[i]=100,存在最大的k=1使得opt[k]=192>100,那么f[i]=k+1=2

    此时f[i]>opt_n,更新opt

    opt_n++; //opt_n=2
    opt[opt_n]=h[i] //opt[2]=100
  3. i=4,h[i]=91,存在最大的k=2使得opt[k]=100>91,那么f[i]=k+1=3

    此时f[i]>opt_n,更新opt

    opt_n++; //opt_n=3
    opt[opt_n]=h[i] //opt[3]=91
  4. i=5,h[i]=149,存在最大的k=1使得opt[k]=192>149,那么f[i]=1+1=2

    此时f[i]<opt_n,即 当前序列长度 小于 opt中已知的最长序列的长度;也就是说,前面已经有过长度相同的不升序列。

    需要判断此时h[i]是否大于opt[f[i]],因为opt要存最大的末位数字。

    if(opt[f[i]]<h[i]) //opt[f[i]]=opt[2]=100<149
    opt[f[i]]=h[i]; //opt[2]=149

依此类推,之后的操作直接给出:

i=6,f[i]=3
opt[1]=192|opt[2]=149|opt[3]=146|opt[4]= 0|opt[5]= 0|opt[6]= 0| i=7,f[i]=2
opt[1]=192|opt[2]=159|opt[3]=146|opt[4]= 0|opt[5]= 0|opt[6]= 0| i=8,f[i]=4
opt[1]=192|opt[2]=159|opt[3]=146|opt[4]=137|opt[5]= 0|opt[6]= 0| i=9,f[i]=5
opt[1]=192|opt[2]=159|opt[3]=146|opt[4]=137|opt[5]= 17|opt[6]= 0| i=10,f[i]=2
opt[1]=192|opt[2]=188|opt[3]=146|opt[4]=137|opt[5]= 17|opt[6]= 0|

总结

而对于同长度的序列,要判断h[i]是否能够接在其后,当然只要判断这些序列中最大的末位数字是否比h[i]大。大了就一定可以,小了就一定不可以。所以opt[i]存的是长度为i的不升序列的末位最大数字

于是,计算f[i]只要在k=1..i-1中找满足opt[k]>=h[i]尽可能大的k,接在其后,新的序列长度就为k+1

opt数组是递减的(反证法:因为每个时刻opt[i]都存长度为i的不升序列的末位最大数字,若i<jopt[i]<opt[j],那么为什么opt[i]的值不能为opt[j]这个序列中的第i个元素呢?),所以可以用二分查找最大的k。

在上面这些操作后,时间复杂度就顺利地降成了O(nlogn)。(´▽` )

附:完整代码

#include<cstdio>
#define reg register
using namespace std;
int n,h[100001],f[100001],opt[100001],opt_n;
int s(int i){
//二分查找最大的k使得opt[k]>=h[i]
int l=1,r=n,m;
while(l<=r){
m=(l+r)/2;
if(opt[m]>=h[i]){
if(m+1<=r && opt[m+1]>=h[i])
l=m+1;
else return m;
}else r=m-1;
}
return 0;
}
void dp(){
//opt[i]:长度是i的最长不升子序列所有子串中末尾最大的那个数,
//根据这个数字,我们可以容易知道,
//只要当前考察的这个数比opt[i]小,那么当前这个数一定能通过opt[i]构成一个长度为i+1的下降子序列。
int k;
opt_n=1;
opt[1]=h[1];f[1]=1;
for(reg int i=2;i<=n;++i){
k=s(i);f[i]=k+1; //求最大的k使得opt[k]>=h[i],则f[i]=k+1
if(f[i]<=opt_n){ //如果f[i]不超过opt_n,则考虑更新opt[f[i]]
if(opt[f[i]]<h[i]) opt[f[i]]=h[i];
}else opt_n++,opt[opt_n]=h[i];
/*--debug--
printf("\n\ni=%d,f[i]=%d\n",i,f[i]);
for(reg int x=1;x<=6;++x)printf("opt[%d]=%3d|",x,opt[x]);
*/
}
}
int main(){
scanf("%d",&n);
for(reg int i=1;i<=n;++i)scanf("%d",&h[i]);
dp();
printf("%d",opt_n);
}

拦截导弹nlogn解法的更多相关文章

  1. 1260:【例9.4】拦截导弹(Noip1999)

    题目来源:http://ybt.ssoier.cn:8088/problem_show.php?pid=1260 1260:[例9.4]拦截导弹(Noip1999) 时间限制: 1000 ms     ...

  2. 拦截导弹类问题 (Codevs4888零件分组POJ1065Wooden Sticks)(LIS及其覆盖问题)

    拦截导弹 题意:求最长不上升子序列长度:求一个序列最少分成几个非增子序. 第一问易求,已知序列a,令f[i]为a前i个元素的最长非增子序的长度,则有 f[i]=max{f[i],f[j]+1} (1& ...

  3. codevs1409 拦截导弹2

    [问题描述]一场战争正在 A 国与 B 国之间如火如荼的展开.B 国凭借其强大的经济实力开发出了无数的远程攻击导弹,B 国的领导人希望,通过这些导弹直接毁灭 A 国的指挥部,从而取得战斗的胜利!当然, ...

  4. nyoj814_又见拦截导弹_DP

    又见拦截导弹 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 大家对拦截导弹那个题目应该比较熟悉了,我再叙述一下题意:某国为了防御敌国的导弹袭击,新研制出来一种导弹拦 ...

  5. 【动态规划】拦截导弹_dilworth定理_最长递增子序列

    问题 K: [动态规划]拦截导弹 时间限制: 1 Sec  内存限制: 256 MB提交: 39  解决: 10[提交][状态][讨论版] 题目描述 张琪曼:“老师,修罗场是什么?” 墨老师:“修罗是 ...

  6. ACM题目————又见拦截导弹

    描述 大家对拦截导弹那个题目应该比较熟悉了,我再叙述一下题意:某国为了防御敌国的导弹袭击,新研制出来一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:它的第一发炮弹能够到达任意的高度,但是以后每一发炮 ...

  7. nyoj------79拦截导弹

    拦截导弹 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 某国为了防御敌国的导弹袭击,发展中一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到 ...

  8. 百练_2945 拦截导弹(DP)

    描述 某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹来袭 ...

  9. nyoj 79 拦截导弹

    拦截导弹 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 某国为了防御敌国的导弹袭击,发展中一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到 ...

随机推荐

  1. ArcGIS 10.3 AddIN编译旧版本项目问题

    ArcGIS 10.1的AddIN项目,后来ArcGIS版本升级为10.3 AddIN项目想做一些细节调整,结果出生成时没有生成esriaddin文件,ArcMap中AddIn Manager中也没有 ...

  2. CAutolock

    顾名思义CAutolock就是自动锁的意思,它可以把它之下的代码区锁住一直到其自身被释放掉    后这块代码区中的公共资源才会被其他线程使用.当然这个代码区能尽量少就尽量少,毕竟不能让其他线    程 ...

  3. php 获取IP地址 并获取坐标lat lng 并获取到所在地区

    函数方法:ps:只能放在服务器上起效果,放在本地是无法起效果的 /* **根据ip获取坐标 ***/ function get_zuobiao(){ $user_IP = ($_SERVER[&quo ...

  4. Python学习之旅(二十四)

    Python基础知识(23):进程和线程(Ⅱ) 一.threadlocal 在多线程环境下,每个线程都有自己的数据 一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响 ...

  5. HTML5 - Canvas【申明:来源于网络】

    HTML5 - Canvas 地址:http://hwaphon.site/

  6. linux文件系统变为只读解决

    linux控制台显示文件系统变为只读,需输密码或者按ctrl+d结束 输入root密码后执行fsck -y /dev/sda1,fsck -y /dev/sda2和fsck -y /dev/sda3等 ...

  7. 快速排序算法回顾 (Python实现)

    #这个也是快速排序-------------------------------------------------- def qsort(list): if list==[]: return [] ...

  8. C#中的一些基础

    值类型与引用类型 值类型包括:[基本数据类型,如int,double,char,bool等][枚举类型enum][结构类型struct] 引用类型包括:[类类型,如基类Object,字符串类Strin ...

  9. vue-cli 搭建的项目,无法用本地IP访问

    项目是用vue-cli搭建的,是基于移动端的,需要在手机上测试的时候发现用ip访问不了,用localhost是可以访问的,网上查资料的解决办法(此为Mac机子的解决办法): 在config文件里面的i ...

  10. C#设计模式(3)——工厂方法模式(转)

    C#设计模式(3)——工厂方法模式   一.引言 在简单工厂模式中讲到简单工厂模式的缺点,有一点是——简单工厂模式系统难以扩展,一旦添加新产品就不得不修改简单工厂方法,这样就会造成简单工厂的实现逻辑过 ...