题目传送门
题目大意:
问题描述
给你一个仅包含数字1,2且长度为n的序列a[1…n].
现在,你可以选择一个区间l,r(1<=l<=r<=n),然后翻转a[l…r]里面的数字.
*比如a={4,5,6},那么翻转a[2…3]之后,a={4,6,5}.
*你可以选择让l==r,那么这个时候a数组没有发生变化.
*设1<=x1< x2< …< xk<=n,且a[x1]<=a[x2]<=…<=a[xk],那么a[x1],a[x2],…,a[xk]就称为一个不下降子序列,k就是这个不下降子序列的长度.
请你选取合适的l,r使得产生的新的序列,它的最长不下降子序列的值最大.
你的任务是输出这个最大值.
输入格式
第一行一个正整数n(1<=n<=2000),表示序列的长度.
第二行有n个整数,第i个数字代表ai(1<=ai<=2).
输出格式
输出只有一个整数,对于给定的序列,请输出题中所要求的最大值.
样例
样例输入
4
1 2 1 2
样例输出
4
样例输入2
10
1 1 2 2 2 1 1 2 2 1
样例输出2
9
样例解释
对于第一个样例,翻转[2,3]这个区间,数组会变成[1,1,2,2].此时最长不下降子序列的长度为4,尝试其他[l,r]的赋值,发现最长不下降子序列的值都不会大于4.
提示
注意只会出现1,2这两个数字.
题解:求出1的前缀和,2的后缀和,以及区间[i,j]的最长不递增子序列。
dp[i][j][0]表示区间i-j以1结尾的最长不递增子序列;
dp[i][j][1]表示区间i-j以2结尾的最长不递增子序列,显然是区间i-j2的个数;
所以转移方程为:
dp[i][j][1] = dp[i][j-1][1] + (a[j]==2);
dp[i][j][0] = max(dp[i][j-1][0], dp[i][j-1][1]) + (a[j]==1);(1<=i<=n,i<=j<=n)
代码如下:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=2000+10;
int arr[maxn];
int q1[maxn],h2[maxn];
int dp[maxn][maxn][2];
int main() {
int n, t;
cin>>n;
for(int i=1; i<=n; i++) {//
cin>>arr[i];
q1[i]+=q1[i-1]+(arr[i]==1);
}
for(int i=n; i>=1; i--) {
h2[i]=h2[i+1]+(arr[i]==2);
}
int ans=-1000000;
for(int i=1;i<=n;i++) {
for(int j=i;j<=n;j++) {
//cout<<"------"<<endl;
dp[i][j][1] = dp[i][j-1][1]+(arr[j]==2);
dp[i][j][0] = max(dp[i][j-1][0], dp[i][j-1][1])+(arr[j]==1);
ans=max(ans,dp[i][j][1]+q1[i-1]+h2[j+1]);
ans=max(ans,dp[i][j][0]+q1[i-1]+h2[j+1]);
}
}
cout<<ans<<endl;
}