用f[i][j][i1][j1]表示序列中i到j,最小值为i1,最大值为j1的子序列最大长度。
先进行预处理:显然要令所有f[i][i][a[i]][a[i]]=1。
另外要注意预处理只有两个且a[i]<a[i+1]的情况,因为这种情况下的翻转下面是枚举不到的。
则有几种状态转移方式:
①直接继承:MAX(f[i][j+1][i1][j1],f[i][j][i1][j1]),MAX(f[i-1][j][i1][j1],f[i][j][i1][j1])。
②直接加入:if(a[j+1]>=j1)MAX(f[i][j+1][i1][a[j+1]],f[i][j][i1][j1]+1),if(a[i+1]<=i1)MAX(f[i-1][j][a[i]][j],f[i][j][i1][j1]+1)。
③考虑翻转:if(a[j+1]<=i1)MAX(f[i-1][j+1][a[j+1]][j1],f[i][j][i1][j1]+1),if(a[i-1]>=j1)MAX(f[i-1][j+1][i1][a[i-1]],f[i][j][i1][j1]+1),if(两者均成立)MAX(f[i-1][j+1][a[j+1]][a[i-1]],f[i][j][i1][j1]+2)
由于这题是多测,我们还要记得每次对f数组进行memset,好像我因为这个问题搞了很久。
这个代码长得不止一点丑:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define ll long long
#define db double
#define ldb long double
#define pli pair<ll,int>
#define mkp make_pair
#define X first
#define Y second
const int N=52;
int n,f[N][N][N][N],a[N],ans;
void MAX(int &x,int y){x=max(x,y);}
int main(){
int i,j,h,i1,j1;
while(~scanf("%d",&n)){
rep(i,1,n)
rep(j,i,n)
rep(i1,1,n)
rep(j1,1,n)
f[i][j][i1][j1]=0;
rep(i,1,n){
scanf("%d",&a[i]);
f[i][i][a[i]][a[i]]=1;
}
rep(i,2,n)if(a[i]<=a[i-1]){
f[i-1][i][a[i]][a[i-1]]=2;
}
ans=0;
rep(h,1,n){
per(i,n-h+1,1){
j=i+h-1;
rep(i1,1,n){
rep(j1,i1,n){
MAX(f[i][j+1][i1][j1],f[i][j][i1][j1]);
MAX(f[i-1][j][i1][j1],f[i][j][i1][j1]);
if(a[j+1]>=j1)MAX(f[i][j+1][i1][a[j+1]],f[i][j][i1][j1]+1);
if(a[i-1]<=i1)MAX(f[i-1][j][a[i-1]][j1],f[i][j][i1][j1]+1);
if(a[j+1]<=i1)MAX(f[i-1][j+1][a[j+1]][j1],f[i][j][i1][j1]+1);
if(a[i-1]>=j1)MAX(f[i-1][j+1][i1][a[i-1]],f[i][j][i1][j1]+1);
if(a[j+1]<=i1&&a[i-1]>=j1)MAX(f[i-1][j+1][a[j+1]][a[i-1]],f[i][j][i1][j1]+2);
}
}
}
} rep(i,1,n)rep(j,i,n)MAX(ans,f[1][n][i][j]);
printf("%d\n",ans);
}
return 0;
}