一个dp题。复杂度是n*logn(n是for循环从0到n遍历,logn是二分查找)
思路:
预处理:
从前往后扫一遍,更新pre数组(记录每一个元素是子串中连续上升的第几个,如果a[i]>a[i-1],则a[i]=1.否则a[i]=a[i-1]+1,a[i]是题目给的)
从后往前扫一遍,更新aft数组(记录每一个元素是子串中连续下降的第几个,如果a[i]>a[i+1],则a[i]=1.否则a[i]=a[i+1]+1)
二分查找:
初始化dp数组为正无穷(0x3f3f3f3f3f3f3f)
从前向后遍历a[i],更新dp数组,dp数组是一个用来记录长度为i的连续上升子串的最后一个元素的最小值的数组。dp[pre[i]]=min(dp[i],a[i]).这样就可得到当前连续上升子串长度的最后一个元素的最小值。
拼接:对于当前i,有最小的dp[1],dp[2]......dp[i-1],(在代码中,把拼接这一步放在更新dp数组前).然后我们用lower_bound在dp数组中寻找到一个比当前a[i]大一点的dp[j],(如果能找到a[i],lower_bound返回的是a[i]坐标,否则返回比a[i]大一点的那个元素的坐标),在dp数组中,小于dp[j]而大于dp数组中其他元素的那个元素一定小于a[i]。例如:
12365 :dp[1]=1 dp[2]=2,dp[3]=3,dp[4]=6,dp[5]=0x3f3f3f3f3f3f3f.对于a[5],二分找到的大于等于a[5]的dp[j]=6,j=4
ans=max(ans,j-1+aft[i]),其中j-1+aft[i]=4-1+1=4
123556:dp[1]=1,dp[2]=2,dp[3]=3,dp[4]=4,dp[5]=0x3f3f3f3f3f3f3f.对于a[5],二分找到的大于等于它的dp[j]=5,j=4
ans=max(ans,j-1+aft[i]),其中j-1+aft[i]=4-1+2=5
12367567:dp[1]=1,dp[2]=2,dp[3]=3,dp[4]=6,dp[5]=0x3f3f3f3f3f3f3f.dp[6]=0x3f3f3f3f3f3f3f,dp[7]=0x3f3f3f3f3f3f3f....对于a[5],二分找到的大于等于它的dp[j]=6,j=4
ans=max(ans,j-1+aft[i]),其中j-1+aft[i]=4-1+3=6
代码:
#include<cstdio>
#include<cstring>
#include<string>
#include<string.h>
#include<stack>
#include<algorithm>
#include<iostream>
#include<math.h>
#define maxn 200008
#define ll long long
#define RPG(i,a,b) for(int i=(a);i<(b);i++)
using namespace std;
int aft[maxn],pre[maxn];
ll dp[maxn],a[maxn];
int main()
{
int T;
scanf("%d",&T);
int n;
while(T--)
{
scanf("%d",&n);
RPG(i,0,n)
scanf("%lld",&a[i]);
pre[0]=1;
RPG(i,1,n)
{
if(a[i]>a[i-1])
pre[i]=pre[i-1]+1;
else
pre[i]=1;
}
aft[n-1]=1;
for(int i=n-2; i>=0; i--)
{
if(a[i]<a[i+1])
aft[i]=aft[i+1]+1;
else
aft[i]=1;
}
int ans=0;
RPG(i,0,n+5)
dp[i]=0x3f3f3f3f3f3f3f3f;
RPG(i,0,n){
int len=lower_bound(dp+1,dp+n+1,a[i])-dp;
ans=max(ans,aft[i]+len-1);
dp[pre[i]]=min(dp[pre[i]],a[i]);
}
printf("%d\n",ans);
}
return 0;
}