继续练习dp,本文讲解nlogn算法
解:
题意就是让我们求一个最长不下降子序列和一个最长上升子序列的长度
STL还是比较方便的,大致介绍一下upper_bound和lower_bound的用法
对于lower_bound,它是返回数组中第一个大于等于x的数,并返回指针。
对于upper_bound,它是返回数组中第一个大于x的数,并返回指针。
lower_bound:
int p=upper_bound(sta1+1,sta1+len+1,a[i],greater<int>())-sta1;
我们可以把指针转化成数组下标,减去数组指针即可。与sort用法类似,注意:
这里我们找的是小于等于x的第一个数,于是用了C++自带的大于函数(实际上是更改了原函数中的<函数),即greater<int>()
我们考虑如何nlogn实现。
我们可以开一个栈,来模拟装入数的过程。最长上升子序列类似,就先模拟最长不上升子序列吧:
首先,第一个数一定是要入栈的,故从第二个开始枚举。设len是栈的长度,则有:
对于当前数,如果小于等于栈首,则直接入栈即可,长度+1.
如果当前数大于栈首,则我们可以找到第一个比它小的数,然后替换它。显然,对于更新后的这个数,后面可以加的数更多,类似于贪心策略。
我们可以一遍扫过,同时处理最长不下降子序列和最长上升子序列。同时,对于最长不下降子序列,我们只要找第一个小于它的数,所以用upper_bound.
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int sta1[110000],sta2[110000]; int a[110000],len=1,len2=1,tot; inline bool read(int &x){ int s=0,w=1; char ch=getchar(); if(ch==EOF)return false; while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }while(ch>=48&&ch<='9'){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }x=s*w; return true; }int buf[500]; inline void write(int x){ int p=0; if(x==0)p++; else while(x){ buf[p++]=x%10; x/=10; }for(int i=p-1;i>=0;i--)putchar('0'+buf[i]); } int main(){ while(read(a[++tot])); tot--; sta1[1]=a[1]; sta2[1]=a[1]; for(int i=2;i<=tot;++i){ if(sta1[len]>=a[i])sta1[++len]=a[i]; else{ int p=upper_bound(sta1+1,sta1+len+1,a[i],greater<int>())-sta1; sta1[p]=a[i]; } if(sta2[len2]<a[i])sta2[++len2]=a[i]; else{ int p=lower_bound(sta2+1,sta2+len2+1,a[i])-sta2; sta2[p]=a[i]; } }printf("%d %d",len,len2); return 0; }