题意:
按序给你一些数,每个数你可以加入双端队列(初始为空),且任何时候都可以弹出元素。求最后能保留在双端队列中的最长不下降子序列长度。
题解:
以 4 5 6 4 2 1 为例:
首先从右至左做最长不下降子序列,设为A序列,这些序列可以每次加到队首。
其次从左至右做最长不上升子序列,设为B序列,这些序列可以每次加到队尾。
那么A和B各选一个序列拼成答案,由于一个从队首进一个从队尾进所以两者的顺序不要紧。
对于每个A序列,B序列选择最小的数不小于A的最大数的最长的那个,如4 4 2 1(A)和4 5 6(B),但是当B的最小数(4)和A的最大数(4)相等时,就有一些是重复了,就减去共有的(1个4),所以求子序列时还要计算最大(最小)的数有几个。
//Time:765ms
//Memory:2644KB
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <map>
#define MAXN 100010
#define INF 1000000007
#define FI first
#define SE second
using namespace std;
pair<int,int> ma[2][MAXN];
int arr[MAXN],tmp[MAXN];
int sum[MAXN],lis[MAXN],ltop;
bool cmp1(const int&a,const int&b)
{
return a<b;
}
bool cmp2(const int&a,const int&b)
{
return a>b;
}
int bs2(int key)
{
int l=0,mid,r=ltop;
while(l<r)
{
mid=(l+r)/2;
if (key==lis[mid]) return mid;
if(lis[mid]<key) l=mid+1;
else r=mid;
}
return l;
}
int bs(int key,int r,bool cmp(const int&,const int&))
{
int l=0,mid;
while(l<r)
{
mid=(l+r+1)/2;
if(cmp(key,tmp[mid])) r=mid-1;
else l=mid;
}
return l;
}
void mfind(pair<int,int> ma[],int n,bool cmp(const int&,const int&))
{
int pos,p2;
for(int i=0;i<n;++i)
{
pos=bs(arr[i],n,cmp);
if(cmp(arr[i],tmp[pos+1])) tmp[pos+1]=arr[i];
p2=bs2(arr[i]);
ma[p2+1].FI=pos+1,++ma[p2+1].SE;
}
}
int main()
{
//freopen("/home/moor/Code/input","r",stdin);
int ncase,n,ans;
scanf("%d",&ncase);
while(ncase--)
{
ans=0;
scanf("%d",&n);
for(int i=0;i<n;++i) scanf("%d",&arr[n-1-i]),lis[i]=arr[n-1-i];
sort(lis,lis+n);
ltop=0;
for(int i=1;i<n;++i)
if(lis[i]!=lis[i-1])
lis[++ltop]=lis[i];
memset(tmp,0,sizeof(tmp));
tmp[0]=INF;
mfind(ma[0],n,cmp2);//up
memset(tmp,0x3f,sizeof(tmp));
tmp[0]=0;
mfind(ma[1],n,cmp1);//down
sum[0]=0;
++ltop;
for(int i=1;i<=ltop;++i) sum[i]=max(sum[i-1],ma[1][i].FI);
for(int i=1;i<=ltop;++i)
{
ans=max(ans,sum[i-1]+ma[0][i].FI);
ans=max(ans,ma[1][i].FI+ma[0][i].FI-min(ma[1][i].SE,ma[0][i].SE));
}
printf("%d\n",ans);
}
return 0;
}