线性dp--最长上升子序列变形

A Twisty Movement

洛谷链接:https://www.luogu.com.cn/problem/CF933A

codeforce链接:https://codeforces.com/problemset/problem/933/A

题面翻译

给定一个序列 A,你可以翻转其中的一个区间内的数,求翻转后的序列的最长不下降子序列的长度。(|A|2000,1ai2

感谢@touristWang 提供的翻译

题目描述

A dragon symbolizes wisdom, power and wealth. On Lunar New Year's Day, people model a dragon with bamboo strips and clothes, raise them with rods, and hold the rods high and low to resemble a flying dragon.

A performer holding the rod low is represented by a 1 , while one holding it high is represented by a 2 . Thus, the line of performers can be represented by a sequence a1,a2,...,an .

Little Tommy is among them. He would like to choose an interval [l,r] ( 1<=l<=r<=n ), then reverse al,al+1,...,ar so that the length of the longest non-decreasing subsequence of the new sequence is maximum.

A non-decreasing subsequence is a sequence of indices p1,p2,...,pk , such that p1<p2<...<pk and ap1<=ap2<=...<=apk . The length of the subsequence is k .

输入格式

The first line contains an integer n (1<=n<=2000) , denoting the length of the original sequence.

The second line contains n space-separated integers, describing the original sequence a1,a2,...,an (1<=ai<=2,i=1,2,...,n) .

输出格式

Print a single integer, which means the maximum possible length of the longest non-decreasing subsequence of the new sequence.

样例 #1

样例输入 #1

4
1 2 1 2

样例输出 #1

4

样例 #2

样例输入 #2

10
1 1 2 2 2 1 1 2 2 1

样例输出 #2

9

提示

In the first example, after reversing [2,3] , the array will become [1,1,2,2] , where the length of the longest non-decreasing subsequence is 4 .

In the second example, after reversing [3,7] , the array will become [1,1,1,1,2,2,2,2,2,1] , where the length of the longest non-decreasing subsequence is 9 .









注意:解析扒的这位佬的 https://www.luogu.com.cn/article/fv3mhsdw

分析

考虑 DP

由于序列只有两个数,那么最终的非降子序列一定是 {1,1,1,2,2,,2} 这样的形式。可以这样表示:[1][2],表示分别由 12 组成的序列。其中这两个部分可能为空。

如果翻转后得到 [1][2],那么翻转前的子序列应该是 [1][2][1][2] 的形式,使得交换中间两个子序列后变成非降子序列。同样的,这些序列也可以为空。

因为最多交换一次,所以题目变成了找原序列的最长子序列,且形式为 [1][2][1][2]

一共有 4 部分,分别将其标号为 03。设状态 fi,j(j[0,3]) 表示序列前 i 个数中,选择前 j 个序列的最长长度。接下来考虑转移。

  • fi,0,前 0 个部分:

    如果第 i 个数为 1,那么可以把这个数和以前的拼起来。答案为 fi1,0+1

    否则,如果第 i 个数为 2,那么这个数不可以作为第 0 部分。答案为 fi1,0

    因此 fi,0={fi1,0+1amp;(ai=1)fi1,0amp;(ai=2)

  • fi,1,前 1 个部分。

    如果第 1 个部分为空,那么答案为 fi,0。以下都是不空的情况。

    如果第 i 个数为 2,那么可以把这个数和以前的拼起来。答案为 fi1,1+1

    否则,如果第 i 个数为 1,那么这个数不可以作为第 1 部分。答案为 fi1,1

    因此 fi,1={max(fi,0,fi1,1+1)amp;(ai=2)max(fi,0,fi1,1)amp;(ai=1)

  • fi,2,前 2 个部分。

    如果第 2 个部分为空,那么答案为 fi,1。以下都是不空的情况。

    如果第 i 个数为 1,那么可以把这个数和以前的拼起来。答案为 fi1,2+1

    否则,如果第 i 个数为 2,那么这个数不可以作为第 2 部分。答案为 fi1,2

    因此 fi,2={max(fi,1,fi1,2+1)amp;(ai=1)max(fi,1,fi1,2)amp;(ai=2)

  • fi,3,前 3 个部分。

    如果第 3 个部分为空,那么答案为 fi,2。以下都是不空的情况。

    如果第 i 个数为 2,那么可以把这个数和以前的拼起来。答案为 fi1,3+1

    否则,如果第 i 个数为 1,那么这个数不可以作为第 3 部分。答案为 fi1,3

    因此 fi,3={max(fi,2,fi1,3+1)amp;(ai=2)max(fi,2,fi1,3)amp;(ai=1)

最后答案为 fn,3

AC 代码:

#include <iostream>
using namespace std;
const int N = 2010;
int a[N];
int f[N][4];
int n;

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i]; 

	for (int i = 1; i <= n; i++) 
	{
		f[i][0] = f[i - 1][0] + (a[i] == 1);
		f[i][1] = max(f[i][0], f[i - 1][1] + (a[i] == 2));
		f[i][2] = max(f[i][1], f[i - 1][2] + (a[i] == 1));
		f[i][3] = max(f[i][2], f[i - 1][3] + (a[i] == 2));
	}
	cout << f[n][3]; 
	return 0;
}
posted @   星竹z  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示