单调序列 解题报告
单调序列
\(\tt{GISPZJZ}\) 有一个长度为 \(n\) 的序列 \(a_1,a_2,\dots,a_n\)。 序列的所有元素都是 \(1\) 或者 \(2\)。
我们称一序列是该序列的不下降子序列 \(p_1,p_2,\dots,p_k\), 满足 \(1\le p_1<p_2<p_3<\dots <p_k\le n\), 且\(a_{p_1}\le a_{p_2}\le \dots \le a_{p_n}\)。
现在 \(\tt{GISPZJZ}\) 可以选择序列中的一段区间\([L,R]\), 然后将整段反转, 例如挑选区间\([2,4]\),可以将序列\((a_1,a_2,a_3,a_4,a_5)\)变换为\((a_1,a_4,a_3,a_2,a_5)\)。 在此基础上, \(\tt{GISPZJZ}\) 希望在反转后, 序列的最长不下降子序列最长。 当然, \(\tt{GISPZJZ}\) 也可以选择不反转任何区间。 现在要求出最优情况下, 序列的最长不下降子序列的长度。
输入:
第一行一个正整数 \(n\)。
第二行 \(n\) 个数, 分别为 \(a_1,a_2,\dots,a_n\), 满足 \(1\le a_i\le 2\)。
输出:
一行一个正整数 \(x\), 表示答案。
数据范围:
对于\(10\%\)的数据, \(1\le n\le 10\)。
对于\(40\%\)的数据, \(1\le n\le 200\)。
对于\(70\%\)的数据, \(1\le n \le 2000\)。
对于\(100\%\)的数据, \(1\le n\le 100000\)。
思路还是很巧妙的。
最开始想把一串的缩在一起做,发现讨论起来非常麻烦。
观察了一下发现翻转后不就是把\(000111\)什么的变成了可以选\(000111000111\)这样类似的了吗?
于是直接划分一下状态\(\tt{DP}\)即可,是一个非常好的思路啊。
Code:
#include <cstdio>
const int N=1e5+10;
int a[N],dp[N][4],n,ans;
int max(int x,int y){return x>y?x:y;}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i),--a[i];
for(int i=1;i<=n;i++)
{
dp[i][0]=dp[i-1][0]+(a[i]==0);
dp[i][1]=max(dp[i-1][0],dp[i-1][1])+(a[i]==1);
dp[i][2]=max(dp[i-1][1],dp[i-1][2])+(a[i]==0);
dp[i][3]=max(dp[i-1][2],dp[i-1][3])+(a[i]==1);
ans=max(max(dp[i][0],dp[i][1]),max(dp[i][2],dp[i][3]));
}
printf("%d\n",ans);
return 0;
}
2017.11.2