#189. 转啊转

用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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值