题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是 ≤ 50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
1行,若干个整数(个数≤100000)
输出格式
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
思路:
第一问就是求最长不上升子序列,由于数据是100000,用 n2 的普通DP不行,用贪心二分优化的 nlogn 算法。
第二问也是一个贪心,用q[i]表示用到的第i个系统的最后一个元素,对每一个输入的元素进行判断,如果当前存在的系统满足加入这个值的条件,那么就用现有的,否则重新开一个系统。
如果有多个满足条件的系统,那么选择哪一个呢,很明显是选择第一个大于等于这个的值的系统。
如何证明q数组是有序的 : 对于每个新加的系统,必有之前的所有系统的最后一个值小于当前要加入的值,所以q数组是正序的。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N],f[N],q[N];
int main()
{
int n = 1;
while(cin >> a[n]) n++;
n--;
int len = 0;
for(int i = 1;i <= n;i++)
{
int l = 0,r = len;
while(l < r) // 找到最后一个大于等于 a[i] 的数,用大数来更新后面的小数
{
int mid = l+r+1 >> 1;
if(f[mid] >= a[i]) l = mid;
else r = mid - 1;
}
len = max(r+1, len);
f[r+1] = a[i];
}
cout << len << endl;
int cnt = 0;
for(int i = 1;i <= n;i++)
{
int k = 1;
while(q[k] < a[i] && k <= cnt) k++; // 找到第一个大于等于a[i]的系统
q[k] = a[i];
if(k > cnt) cnt++;
}
cout << cnt << endl;
return 0;
}