Wraith_Fiee的博客

P1020 导弹拦截

2021-11-13 · 3 min read
贪心 二分

原题传送门

思路:贪心+二分,复杂度O(NlogN)O(NlogN)
求该套系统最多能拦截导弹就是求这个序列的最长非上升子序列的长度
求一共需要多少套系统就是求这个序列的最长非上升子序列的个数
因为最长非上升子序列的个数等于最长上升子序列(LIS)的长度所以也就完成了对问题二的转化
利用贪心的思想,对于一个上升子序列,当前最后一个元素越小,越容易添加新的元素,那么LIS越长
简单来说,对于我们要维护的ff数组,显然ff数组是递增的,那么对于每一个a[i]a[i]来说,如果a[i]>f[len]a[i]>f[len],那么就将a[i]a[i]加入ff数组的末尾,反之 如果a[i]<=f[len]a[i]<=f[len],那么就查找到ff数组中第一个大于等于a[i]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];
}

由于ff数组是单调的,所以可以用二分查找,查找时间复杂度 O(logN)O(logN),总体复杂度 O(NlogN)O(NlogN)
使用自带的STL函数可以省略手写二分,但由于 upperupper_boundbound 要求的是递增序列,但求最长非上升子序列时ff数组显然是递减的,因此需要重新定义函数,这里利用C++提供的大于函数 greater<int>()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;
}
在吹不出褶皱的日子里发光
Powered by Gridea