导弹拦截 \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;
}