https://codeforces.com/gym/102114

H. Hills And Valleys

 

题意:给定一个长为 1n1051\le n\le 10^5 的数组 AA0Ai90\le A_i\le 9,要求翻转一个区间,使得新数组的最长不下降子序列最长。输出长度与翻转的区间。

dp

先假设不翻转。

设有数组 B[10]={0,1,2,3,4,5,6,7,8,9}B[10]=\{0,1,2,3,4,5,6,7,8,9\},则数组 AA 的最长不下降子序列可以视为与 BB 的特殊的最长公共子序列,特殊之处在于 AA 的子序列中连续的相同数字可以视为一个数字。

转移方程也与普通最长公共子序列类似:原来是 dp[i][j]=dp[i1][j1]+1dp[i][j]=dp[i-1][j-1]+1,现在是 dp[i][j]=dp[i1][j]+1dp[i][j]=dp[i-1][j]+1

现在要翻转 AA 数组,变成翻转 BB 数组,因为 BB 很短,所以直接枚举翻转区间。再求出这个特殊的最长公共子序列,同时要记录下dp路径,看与当前翻转区间对应的是 AA 数组中哪一个区间。

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <bits/stdc++.h>

#define debug(x) cout << #x << ":\t" << (x) << endl;
using namespace std;
#define ll long long
#define ull unsigned ll
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const ll inv2 = (mod + 1) / 2;
const double eps = 1e-8;
const double PI = acos(-1);
typedef pair<int, int> pii;
int T, n;
char s[N];
int a[N], b[30];
int v[N], l[N], r[N], c[N];
int dp[N][30];
pii f[N][30];

int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
scanf("%s", s + 1);
for (int i = 1; i <= n; i++) {
a[i] = s[i] - '0';
}
int m = 1;
l[1] = 1;
v[1] = a[1];
for (int i = 2; i <= n; i++) {
if (a[i] != a[i - 1]) {
v[++m] = a[i];
l[m] = i;
r[m - 1] = i - 1;
c[m - 1] = i - l[m - 1];
}
}
r[m] = n;
c[m] = n - l[m] + 1;
for (int i = 1; i <= 20; i++)b[i] = (i - 1) / 2;
int ans = 0, L = 0, R = 0;
for (int i = 1; i <= 20; i++) {
for (int j = i; j <= 20; j++) {
dp[0][0] = dp[0][1] = dp[1][0] = 0;
reverse(b + i, b + j + 1);
for (int k = 1; k <= m; k++) {
for (int p = 1; p <= 20; p++) {
dp[k][p] = dp[k - 1][p];
f[k][p] = f[k - 1][p];
if (dp[k][p - 1] > dp[k][p]) {
dp[k][p] = dp[k][p - 1];
f[k][p] = f[k][p - 1];
}
if (v[k] == b[p] && dp[k - 1][p] + c[k] > dp[k][p]) {
dp[k][p] = max(dp[k][p], dp[k - 1][p] + c[k]);
f[k][p] = {k, p};
}
}
}
if (dp[m][20] > ans) {
int tl=0,tr=0;
pii u = {m, 20};
while (u.first > 0 && u.second > 0) {
u = f[u.first][u.second];
if (u.second == i)tl = l[u.first];
if (u.second == j)tr = max(tr, r[u.first]);
u.first--;
}
if(tl && tr){
ans = dp[m][20];
L = tl;
R = tr;
}
}
reverse(b + i, b + j + 1);
}
}
printf("%d %d %d\n", ans, L, R);
}
return 0;
}