線性dp--最長上升子序列變形

星竹z發表於2024-04-25

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;
}

相關文章