题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
1行,若干个整数(个数≤100000)
输出格式
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
解决问题
在luogu上这道题被加强过了,分为两问.
第一问
显然,对于一套设备来说,它能防御的是一个不上升序列,也就是说,要求出一套设备最多能拦截多少导弹,只要在导弹序列中求出最长不上升子序列的长度就可以了.值得注意的一点是,数据范围不允许我们用n2做法求子序列,因此要采用二分查找的方式以nlogn的时间复杂度求解.
第二问
这个问题可以在脑内想象一下.
现在有一列导弹飞来,高度有高有低.为了拦截导弹,第一颗导弹必须由第一套设备拦截下来,但是从此以后它就不能拦住比这一颗低的导弹了.如果之后有一颗比当前能拦截的高度还要高的导弹飞来,就只能启用一套新的设备来拦截它了.这些都是显而易见的.但是问题来了,如果一颗不是很高的导弹飞来,有不止一套设备能拦住它,那应该由谁来呢?
考虑我们现在所拥有的设备,每一台设备都可能在后面拦截很多导弹,但是每一台设备的"前途"是不一样的.显然,目前能拦截的高度最高的设备更有前途能拦截下以后的较高的导弹.而最高高度较低的设备就前途不如这一台好.从另一个角度来说,高度高的设备,如果拦截这一颗导弹,它的最高高度会下降很多,而高度较低的,则会下降较少,显然让较低的来比较划算.当然我们仅仅是在能拦截这颗导弹的设备范围内查找.为了提高效率,我们采用二分查找或者直接lower_bound来查找.
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n, h[100005], b[100005], target = 1, system[100005];
void findpos(int l, int r, int aim)//二分查找求最长不上升子序列
{
int mid = (l + r) / 2;
if(l == mid)
{
b[mid + 1] = aim;
return;
}
if(aim > b[mid])
{
findpos(l, mid, aim);
}
else if(aim <= b[mid])
{
findpos(mid, r, aim);
}
}
int main()
{
int qwq = 1, x;
while (scanf("%d", &x) == 1) {
h[qwq] = x;
qwq++;
}
qwq--;
b[1] = h[1];
for (int i = 2; i <= qwq; i++) {
if(h[i] <= b[target])
b[++target] = h[i];
else
findpos(0, target, h[i]);
}
cout << target << endl;
/*********以上第一问,以下第二问*********/
int nos = 1;//nos,即number of systems,目前已经采用的设备台数
sys[1] = h[1];//sys数组存放每一台设备的最高高度
for(int i = 2; i <= qwq; i++) {
if(h[i] > sys[nos]) {//因为新设备是因为导弹太高而被启用,所以新设备的最高高度一定是最大的
nos++;
sys[nos] = h[i];
}//启用更新的新设备
else {
int t = lower_bound(sys + 1, sys + 1 + nos, h[i]) - sys;//找到能拦截的高度最小的设备
sys[t] = h[i];
}
}
cout << nos << endl;
return 0;
}
欢迎批评和指正!