TIOJ-1924

有三個人(P, E, C)走在路上很無聊,於是他們開始了一個遊戲。

他們將要走的路是一條筆直的路徑,依序有$N\le 2\times 10^6$顆石塊排成一排。這$N$顆石塊很特別,每一顆都只能被P、E、C其中一個人撿起。之後P、E、C會在走過這條路時選一個區間開始撿石塊,將區間內所有可以被他撿起的石塊都拿起來。然而為了避免P、E、C三個人在路途中打架,他們所選定的區間兩兩交集必須要是空的。

說石持那十塊,P、E、C三個人已經走完這條路並且撿好石塊了。請計算他們三人所持有的石塊個數總和最大是多少。


可以發現如果路上有任何地方沒被三個人的區間涵蓋到,那把這個地方也給涵蓋一定不會比較差,所以問題就變成把整條路切成三段,每段分配給其中一人。
考慮$dp[N][X][{S}]$,代表第$N$個石頭是分配給$X$,且已經$S$內的人皆已經被分配過了。

轉移時,可能前一顆石頭也是給$X$,或者是給$S$內除了$X$的人。

而這題會卡記憶體,所以要滾動,設$X$為人數(在這題中為3),時間複雜度$\mathcal{O}(NX^22^X)$,空間複雜度$\mathcal{O}(2^XX)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*
-------------- | /
| | /
| | /
| * |/ | | ------ *
| | | | / \
| | |\ | | | |\ |
\ | | | \ | | | | \ |
\ | | | \ | | \ / \ |
V | | \ \__/| ----- \ |
*/
#include <bits/stdc++.h>
using namespace std;

#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
/*-----------------------------------------------------------------------------------------------------*/

int dp[2][1 << 3][3];

signed main() {
EmiliaMyWife

int n;
cin >> n;
string s;
cin >> s;
int a = 0, b = 1;
for(int i = 1; i <= n; i++) {
swap(a, b);
char c = s[i - 1];
int owo;
if(c == 'P')
owo = 0;
if(c == 'C')
owo = 1;
if(c == 'E')
owo = 2;
for(int j = 0; j < (1 << 3); j++) {
for(int k = 0; k < 3; k++) {
if(!(j >> k & 1))
continue;
dp[a][j][k] = dp[b][j][k];
for(int w = 0; w < 3; w++) {
if(j >> w & 1)
dp[a][j][k] = max(dp[a][j][k], dp[b][j ^ (1 << k)][w]);
}
if(k == owo)
dp[a][j][k]++;
}
}
}
int ans = 0;
for(int i = 0; i < (1 << 3); i++)
for(int j = 0; j < 3; j++)
ans = max(ans, dp[a][i][j]);
cout << ans << '\n';

return 0;
}