P1020 [NOIP1999 普及组] 导弹拦截
nlogn解决最长上升/不下降/下降/不上升子序列问题。
设f[len]表示子序列长度为len时序列最后一个数是什么。
那么可以根据具体情况来讨论,一种是直接++len,将其存在数列最后,另一种是用更优的更新不优的。
此题中第一问不难看出是求一个最长不下降子序列,而第二问,相当于求一个最长上升子序列:
我们假定对于a[i]序列已经分成了k组,那么在这k组中,一定会在k+1组中找到一个数比k组中的某一个(几个)数大(否则可将k与k+1组合并),那么相当于k个组为一个单调递增的序列,求这个增序列的最大长度即为答案。
自己总结如下:
严格单调上升或下降子序列,找的时候用lower_bound,反之用upper_bound。(用cmp可以从找第一个大于/大于等于的数转为找第一个小于/小于等于的数)
严格单调上升或下降子序列,用>或<,反之用>=或<=。
(在下面程序中进行修改)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100005;
typedef long long ll;
int cnt,tot,n;
int a[N],f[N],d[N];
bool cmp(int a,int b){
return a>b;
}
int main(){
while(cin>>a[++n]);
n--;
memset(f,0x3f3f,sizeof(f));
for(int i=1;i<=n;i++){
if(a[i]<=f[cnt]) f[++cnt]=a[i];//不上升
else f[upper_bound(f+1,f+cnt+1,a[i],cmp)-f]=a[i];
if(a[i]>d[tot]) d[++tot]=a[i];
else d[lower_bound(d+1,d+tot+1,a[i])-d]=a[i];
}
printf("%d\n%d",cnt,tot);
return 0;
}
//lower_bound会找出序列中第一个大于等于x的数
//upper_bound会找出序列中第一个大于x的数