题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
1行,若干个整数(个数≤100000)
输出格式
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
(1)这套系统最多能拦截多少导弹
我们只需找出所给数组的最长不上升子序列即可;
(2)如果要拦截所有导弹最少要配备多少套这种导弹拦截系统
因为每个导弹拦截系统拦截的导弹每一发炮弹都不能高于前一发的高度, 因此我们只需统计出所给数组中的数字(该数字大于前面所有的数字)的数量即可, 既找出所给数组的最长上升子序列。
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int f[N], g[N], cnt, sum, n, a[N];
//a数组存储输入的数据, f存储最长不上升子序列, g数组存储最长上升子序列
//sum标记最长不上升子序列的末尾值的下标, cnt标记最长不上升子序列的末尾值的下标
int main()
{
while (cin >> a[n]) n++;//存储输入数据
f[0] = g[0] = a[0];//将第一个数字存进子序列中
for (int i = 1; i < n; i++)
{
if (a[i] <= f[sum])
f[++sum] = a[i];
//因为最长不上升子序列, 所以发现小于或等于子序列末尾值的都塞进子序列末尾
else
{
int l = 0, r = sum;
while (l < r)
{
int m = l + r >> 1;
if (f[m] < a[i]) r = m;
else l = m + 1;
}
//利用二分找第一个小于a[i]的值的下标
f[l] = a[i];//更新找到的下标的值
}
//以下用同上种方式找出最长上升子序列
if (a[i] > g[cnt])
g[++cnt] = a[i];
else
{
int l = 0, r = cnt;
while (l < r)
{
int m = l + r >> 1;
if (g[m] >= a[i]) r = m;
else l = m + 1;
}
g[l] = a[i];
}
}
cout << sum + 1 << endl << cnt + 1;//因为子序列下标从0开始,所以求出的值加一
return 0;
}