线性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 & (a_i = 1)\\ f_{i - 1, 0} & (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) & (a_i = 2)\\ \max(f_{i, 0}, f_{i - 1, 1}) & (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) & (a_i = 1)\\ \max(f_{i, 1}, f_{i - 1, 2}) & (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) & (a_i = 2)\\ \max(f_{i, 2}, f_{i - 1, 3}) & (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;
}