导弹拦截

98 篇文章 1 订阅
78 篇文章 2 订阅

导弹拦截 ⁡ \operatorname{导弹拦截}

题目链接: luogu P1020 ⁡ \operatorname{luogu\ P1020} luogu P1020

题目

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

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

输入

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

输出

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

样例输入

389 207 155 300 299 170 158 65

样例输出

6
2

提示

为了让大家更好地测试 n 2 n^2 n2 算法,本题开启spj, n 2   100 n^2\ 100 n2 100 分, n l o g n   200 nlogn\ 200 nlogn 200

每点两问,按问给分

思路

这道题是一道最长上升&最长下降子序列。

n 2 n^2 n2 算法就不多说,这里我用了 n l o g n nlogn nlogn 的算法。

我们那最长下降子序列作为例子:
如果一个数比当时最长的下降子序列的最后一个更小,那就直接可以打它。
但是如果不行,那我们就二分枚举当时最长的下降子序列,找到第一个比它小的,跟它换。因为这样可以尽可能的让导弹拦截一定数量时高度更高。

而第二个问题则是要求最长上升子序列。
至于为什么,就是因为一个上升子序列中,每一个数都肯定不是在同一个导弹拦截系统里面的,因为越来越大,导弹打到这个序列的一个数就肯定不能打到别的,一道要再打一次。

所以就求最长下降子序列和最长上升子序列就可以了。

代码

#include<cstdio>
#include<iostream>

using namespace std;

int n, a[100001], xia[100001], shang[100001], lef, righ, xia_num, shang_num;

int main() {
//	freopen("check.txt", "r", stdin);
	
	xia[0] = 2147483647;
	shang[0] = -2147483647;//初始化
	
	while (scanf("%d", &a[++n]) != EOF) {//读入
		if (a[n] <= xia[xia_num]) {//打完上一个就可以打这个
			xia_num++;
			xia[xia_num] = a[n];
		}
		else {//二分出可以从哪个过来
			lef = 0, righ = xia_num;
			while (lef < righ) {
				int mid = (lef + righ) >> 1;
				if (xia[mid] >= a[n]) lef = mid + 1;
					else righ = mid;
			}
			xia[lef] = max(xia[lef], a[n]);
		}
		
		if (a[n] > shang[shang_num]) {//跟上面一个道理
			shang_num++;
			shang[shang_num] = a[n];
		}
		else {
			lef = 0, righ = shang_num;
			while (lef < righ) {
				int mid = (lef + righ) >> 1;
				if (shang[mid] >= a[n]) righ = mid;
					else lef = mid + 1;
			}
			shang[lef] = min(shang[lef], a[n]);
		}
	}
	
	printf("%d\n%d", xia_num, shang_num);//输出
	
//	fclose(stdin);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值