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

A Twisty Movement

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

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

题面翻译

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

感谢@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 $ a_{1},a_{2},...,a_{n} $ .

Little Tommy is among them. He would like to choose an interval $ [l,r] $ ( $ 1<=l<=r<=n $ ), then reverse $ a_{l},a_{l+1},...,a_{r} $ 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 $ p_{1},p_{2},...,p_{k} $ , such that $ p_{1}<p_{2}<...<p_{k} $ and $ a_{p1}<=a_{p2}<=...<=a_{pk} $ . 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 $ a_{1},a_{2},...,a_{n} $ $ (1<=a_{i}<=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, \dots 1, 2, 2, \dots, 2\}\) 这样的形式。可以这样表示:\([1][2]\),表示分别由 \(1\)\(2\) 组成的序列。其中这两个部分可能为空。

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

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

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

  • \(f_{i, 0}\),前 \(0\) 个部分:

    如果第 \(i\) 个数为 \(1\),那么可以把这个数和以前的拼起来。答案为 \(f_{i - 1, 0} + 1\)

    否则,如果第 \(i\) 个数为 \(2\),那么这个数不可以作为第 \(0\) 部分。答案为 \(f_{i - 1, 0}\)

    因此 \(f_{i, 0} = \left\{\begin{matrix} f_{i - 1, 0} + 1 &amp; (a_i = 1)\\ f_{i - 1, 0} &amp; (a_i = 2)\end{matrix}\right.\)

  • \(f_{i, 1}\),前 \(1\) 个部分。

    如果第 \(1\) 个部分为空,那么答案为 \(f_{i, 0}\)。以下都是不空的情况。

    如果第 \(i\) 个数为 \(2\),那么可以把这个数和以前的拼起来。答案为 \(f_{i - 1, 1} + 1\)

    否则,如果第 \(i\) 个数为 \(1\),那么这个数不可以作为第 \(1\) 部分。答案为 \(f_{i - 1, 1}\)

    因此 \(f_{i, 1} = \left\{\begin{matrix} \max(f_{i, 0}, f_{i - 1, 1} + 1) &amp; (a_i = 2)\\ \max(f_{i, 0}, f_{i - 1, 1}) &amp; (a_i = 1)\end{matrix}\right.\)

  • \(f_{i, 2}\),前 \(2\) 个部分。

    如果第 \(2\) 个部分为空,那么答案为 \(f_{i, 1}\)。以下都是不空的情况。

    如果第 \(i\) 个数为 \(1\),那么可以把这个数和以前的拼起来。答案为 \(f_{i - 1, 2} + 1\)

    否则,如果第 \(i\) 个数为 \(2\),那么这个数不可以作为第 \(2\) 部分。答案为 \(f_{i - 1, 2}\)

    因此 \(f_{i, 2} = \left\{\begin{matrix} \max(f_{i, 1}, f_{i - 1, 2} + 1) &amp; (a_i = 1)\\ \max(f_{i, 1}, f_{i - 1, 2}) &amp; (a_i = 2)\end{matrix}\right.\)

  • \(f_{i, 3}\),前 \(3\) 个部分。

    如果第 \(3\) 个部分为空,那么答案为 \(f_{i, 2}\)。以下都是不空的情况。

    如果第 \(i\) 个数为 \(2\),那么可以把这个数和以前的拼起来。答案为 \(f_{i - 1, 3} + 1\)

    否则,如果第 \(i\) 个数为 \(1\),那么这个数不可以作为第 \(3\) 部分。答案为 \(f_{i - 1, 3}\)

    因此 \(f_{i, 3} = \left\{\begin{matrix} \max(f_{i, 2}, f_{i - 1, 3} + 1) &amp; (a_i = 2)\\ \max(f_{i, 2}, f_{i - 1, 3}) &amp; (a_i = 1)\end{matrix}\right.\)

最后答案为 \(f_{n, 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 @ 2024-04-25 11:57  星竹z  阅读(12)  评论(0编辑  收藏  举报