【原创】【NOIP1999】拦截导弹

来源:互联网 发布:广联达软件下载教程 编辑:程序博客网 时间:2024/06/05 03:25


[NOIP1999]拦截导弹

时间限制: 1 Sec  内存限制: 64 MB
提交: 666  解决: 233

题目描述

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

输入

第1行:依次输入若干个导弹的高度H(1≤H≤30000),导弹的个数N≤5000

输出

第1行:一个整数,表示单枚炮弹能拦截多少导弹 第2行:一个整数,表示拦截所有导弹最少要配备多少套这种导弹拦截系统

样例输入

389 207 155 300 299 170 158 65

样例输出

6
2

提示

第2问最直观的算法是贪心,但是反例也容易找到,如:

6 5 1 7 3 2

如果第一次打6 5 3 2,显然还要打两次,而最好的方案是6 5 1/7 3 2。




这是一道很经典的题目,题里涵盖了对动归,搜索,贪心的考察。那么,就让我们好好地来分析分析这道题目吧。
这道题目有两个小问。先来看看第一个小问。
我在题目中用红色标记出了一些关键信息,透过这些信息,我们可以发现,这个炮弹是从高往低掉,看来我们要找“最长下降序列”?不不不,再看看那几个红得发黄字,“不得高于”!
注意了,是“最长不上升序列”!是“≤”!
别被坑了!(我也被坑了!)
好啦,怎么找这条序列呢?
首先,我们要找的这串数要满足两个条件,“最长”和“不上升”。
就是说这串数每个数都≤上一个数,而且这串数里的数字最多。
那么,我们只需要把每一串都找出来再比较长短就okay了!
You say:啊?那有几千几万种情况你找得完?
Too naive!
为了保证最长,我们要让每一串数的起始位置尽量往前,终止位置尽量往后,没错吧?
所以,我们只需枚举这n个数,以第i个数为这串数的起始位置,看谁串的数多,就可以了。

下一个问题,最少(发)射几(发)(弹才能打完所有的导弹)
~v~手动滑稽中~v~
提示已经告诉我们了,贪心。
提示也说了,不可以打最长不上升序列。
那怎么办呢?
别急,先来分析一下为什么“不可以打最长不上升序列”,
这是题目给的反例:6 5 1 7 3 2
最佳方案是:6 5 1 // 7 3 2
再来一组数据:4 3 4 7 6 2 1 
最佳方案:4 4 2 1 // 3 // 7 6  或者 4 3 2 1 // 4 // 7 6  或者 4 4 // 7 6 2 1 // 3
来,来找规律!
我们可以发现,每一个“//”内都是不上升序列。为了让“//”更少,我们要保证每一条序列的长度都要尽量长
这就是“不可以打最长不上升序列”的原因。我们打掉最长的,但剩下的几组的长度就不一定很长了。
所以,我们只需要再找序列的时候,让它尽可能长,就可以了。
而不是找到最长的一条,打掉;再找一条,再打掉……

讲了这么多,那就详见代码吧:
#include<cstdio>#include<algorithm>using namespace std;int n=1,a[6006],da=-1,db,yx,f[6006],much;void preparation(){while(scanf("%d",&a[n])!=EOF) n++;n--;}void step_one(){for(int i=1;i<=n;i++){int e=0;for(int j=1;j<i;j++)if(a[j]>=a[i] and e<f[j]) e=f[j];f[i]=e+1;if(da<f[i]) da=f[i],db=i;}printf("%d\n",da);}void step_two(){much=a[1],yx=0;for(int i=1;i<=n;i++)if(a[i]>=0) {much=a[i];for(int j=i+1;j<=n;j++)if(a[j]>=0 and a[j]<=much)much=a[j],a[j]=-66;yx++;}printf("%d",yx);}int main(){preparation();step_one();step_two();}



1 0
原创粉丝点击