P1020 导弹拦截
思路:贪心+二分,复杂度\(O(NlogN)\)
求该套系统最多能拦截导弹就是求这个序列的最长非上升子序列的长度
求一共需要多少套系统就是求这个序列的最长非上升子序列的个数
因为最长非上升子序列的个数等于最长上升子序列(LIS)的长度所以也就完成了对问题二的转化
利用贪心的思想,对于一个上升子序列,当前最后一个元素越小,越容易添加新的元素,那么LIS越长
简单来说,对于我们要维护的\(f\)数组,显然\(f\)数组是递增的,那么对于每一个\(a[i]\)来说,如果\(a[i]>f[len]\),那么就将\(a[i]\)加入\(f\)数组的末尾,反之 如果\(a[i]<=f[len]\),那么就查找到\(f\)数组中第一个大于等于\(a[i]\)的位置,替换之
if(f2[len2]<a[i]){
f2[++len2]=a[i];
}
else{
int pos=lower_bound(f2+1,f2+len2+1,a[i])-f2;
f2[pos]=a[i];
}
由于\(f\)数组是单调的,所以可以用二分查找,查找时间复杂度 \(O(logN)\),总体复杂度 \(O(NlogN)\)
使用自带的STL函数可以省略手写二分,但由于 \(upper\)_\(bound\) 要求的是递增序列,但求最长非上升子序列时\(f\)数组显然是递减的,因此需要重新定义函数,这里利用C++提供的大于函数 \(greater<int>()\)
upper_bound(f1+1,f1+len1+1,a[i],greater<int>())-f1;
Code:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int a[N],f1[N],f2[N];
int main(){
int n=1;
while(scanf("%d",&a[n])!=EOF) n++;
n--;
int len1=1,len2=1;
f1[1]=a[1],f2[1]=a[1];
for(int i=2;i<=n;i++){
if(f1[len1]>=a[i]){
f1[++len1]=a[i];
}
else{
int pos=upper_bound(f1+1,f1+len1+1,a[i],greater<int>())-f1;
f1[pos]=a[i];
}
}
cout<<len1<<endl;
for(int i=2;i<=n;i++){
if(f2[len2]<a[i]){
f2[++len2]=a[i];
}
else{
int pos=lower_bound(f2+1,f2+len2+1,a[i])-f2;
f2[pos]=a[i];
}
}
cout<<len2<<endl;
return 0;
}