【原创】【NOIP1999】拦截导弹


[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();
}



  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值