单调序列 解题报告

单调序列

\(\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

转载于:https://www.cnblogs.com/butterflydew/p/9896022.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值