题目
解
对于数列中的某个点:
求从它开始下降的最长子序列,这个序列将倒过来接到某点前面,成为最终的序列的前半段;
求从它开始上升的最长子序列,这个序列将倒过来接到某点后面,成为最终的序列的后半段;
最终答案为某个点两种子序列长度的和再减去1(因为统计了两次自己)
其余的操作其实都可以省略。(无法更优)
Code
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int a[100001],b[100001],ans[10][100001],c[100001];
int n,z,Ans;
void asdf(int cs){
memset(c, 0, sizeof(c)); z = 0;
for(int i = 1; i <= n; ++i){
int now = lower_bound(c+1, c+z+1, b[i]) - c; //c[1-z]中寻第一个小于b[i]的数的位置
c[now] = b[i]; //存入数组
if(now > z) ++z;
ans[cs][n-i+1] = now;
}
}
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; ++i){
scanf("%d",&a[i]);
b[n-i+1] = a[i]; //倒着存,方便借用lower_bound
}
asdf(1); //求某点开始下降的最长子序列,这个序列将倒过来接到某点前面
for(int i = 1; i <= n; ++i)
b[i] = -b[i]; //反过来,方便计算
asdf(2); //求某点开始上升的最长子序列,这个序列将倒过来接到某点后面
//最终某点不变位置,
for(int i = 1; i <= n; ++i)
Ans = max(ans[1][i]+ans[2][i]-1, Ans); //找出最大的
printf("%d", Ans);
}