AcWing 283 多边形
题目大意
题目链接见此283. 多边形 - AcWing题库
题解
高级的合并石子问题,需要解决的问题包括
- 成环
- 乘法
状态设计仿照合并石子即可,令$f_{i, j, 0}$表示区间$[i,j]$的最大值, $f_{i , j, 1}$表示区间$[i, j]$的最小值,维护最小值的原因下面会说
成环解决
常用套路,在[1, n]之后再补上一段[1, N]。第一步的选边删除策略也可以转变成枚举的全区间结点[1, N], [2, N+1], ……,[N, 2N-1],从中找到最大值
1
2
3
4
5
6
7
8
9
10
int maxF = -0x7fffffff;
for (int i = 1; i <= N; i++) {
maxF = max(maxF, F[i][i + N - 1][0]);
}
cout << maxF << endl;
for (int i = 1; i <= N; i++) {
if (F[i][i + N - 1][0] == maxF) {
cout << i << " ";
}
}
乘法解决
乘法带来的改变就体现在负数时带来的影响,最小值与最小值的乘积可能变成最大值,因此需要额外维护区间的最小值
以最大值为例,考虑负数带来的影响
- 左右区间最小值均大于0, $\text{max} \leftarrow \text{max} \times \text{max}$
- 左右区间最大值均小于0,$\text{max} \leftarrow \text{min} \times \text{min} $
- 左区间横跨0,右区间均大于0,, $\text{max} \leftarrow \text{max} \times \text{max} $
- 左区间横跨0,右区间均小于0,$\text{max} \leftarrow \text{min} \times \text{max} $
- 左区间横跨0,右区间横跨0, $\text{max} \leftarrow \text{max} \times \text{max}$
- 左区间均大于0,右区间横跨0, $\text{max} \leftarrow \text{max} \times \text{max}$
- 左区间均大于0,右区间均小于0, $\text{max} \leftarrow \text{max} \times \text{max}$
- 左区间均小于0,右区间均大于0, $\text{max} \leftarrow \text{max} \times \text{min}$
- 左区间均小于于0,右区间横跨0, $\text{max} \leftarrow \text{min} \times \text{min}$
合并来说
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for (int k = 2; k <= N; k++) { // k表示区间长度
for (int i = 1; i <= N * 2 - k + 1; i++) { // i区间右端点
int j = i + k - 1;
for (int l = i; l < j; l++) {
if (op[l+1] == 't') {
F[i][j][0] = max(F[i][j][0], F[i][l][0] + F[l + 1][j][0]);
F[i][j][1] = min(F[i][j][1], F[i][l][1] + F[l + 1][j][1]);
} else {
F[i][j][0] = max(F[i][j][0], F[i][l][0] * F[l + 1][j][0]);
F[i][j][0] = max(F[i][j][0], F[i][l][1] * F[l + 1][j][1]);
F[i][j][0] = max(F[i][j][0], F[i][l][0] * F[l + 1][j][1]);
F[i][j][0] = max(F[i][j][0], F[i][l][1] * F[l + 1][j][0]);
F[i][j][1] = min(F[i][j][1], F[i][l][1] * F[l + 1][j][1]);
F[i][j][1] = min(F[i][j][1], F[i][l][0] * F[l + 1][j][1]);
F[i][j][1] = min(F[i][j][1], F[i][l][1] * F[l + 1][j][0]);
F[i][j][1] = min(F[i][j][1], F[i][l][0] * F[l + 1][j][0]);
}
}
}
}
参考代码
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
//
// Created by Leo on 2023/2/28.
//
#include <bits/stdc++.h>
using namespace std;
// 进阶版合并石子 (环;乘法)
const int MaxN = 60;
// *2 是因为要考虑环的情况, f[n,n,0]max, f[n,n, 1]min 因为有负数和乘法所以也要维护最小
int N, F[MaxN*2][MaxN*2][2], num[MaxN*2];
char op[MaxN*2];
int main() {
cin >> N;
for (int i = 1; i <= N; i++) {
cin >> op[i] >> num[i];
op[i + N] = op[i];
num[i + N] = num[i];
}
// initialization
for (int i = 1; i <= N * 2; i++) {
F[i][i][0] = num[i];
F[i][i][1] = num[i];
}
for (int i = 1; i <= N * 2; i++)
for (int j =1; j <= N * 2; ++j) {
if (i == j) continue;
F[i][j][0] = -0x3f3f3f3f;
F[i][j][1] = 0x3f3f3f3f;
}
// f(i, j, 0) 表示从i到j的最大值, 区间dp开始
for (int k = 2; k <= N; k++) { // k表示区间长度
for (int i = 1; i <= N * 2 - k + 1; i++) { // i区间右端点
int j = i + k - 1;
for (int l = i; l < j; l++) {
if (op[l+1] == 't') {
F[i][j][0] = max(F[i][j][0], F[i][l][0] + F[l + 1][j][0]);
F[i][j][1] = min(F[i][j][1], F[i][l][1] + F[l + 1][j][1]);
} else {
F[i][j][0] = max(F[i][j][0], F[i][l][0] * F[l + 1][j][0]);
F[i][j][0] = max(F[i][j][0], F[i][l][1] * F[l + 1][j][1]);
F[i][j][0] = max(F[i][j][0], F[i][l][0] * F[l + 1][j][1]);
F[i][j][0] = max(F[i][j][0], F[i][l][1] * F[l + 1][j][0]);
F[i][j][1] = min(F[i][j][1], F[i][l][1] * F[l + 1][j][1]);
F[i][j][1] = min(F[i][j][1], F[i][l][0] * F[l + 1][j][1]);
F[i][j][1] = min(F[i][j][1], F[i][l][1] * F[l + 1][j][0]);
F[i][j][1] = min(F[i][j][1], F[i][l][0] * F[l + 1][j][0]);
}
}
}
}
int maxF = -0x7fffffff;
for (int i = 1; i <= N; i++) {
maxF = max(maxF, F[i][i + N - 1][0]);
}
cout << maxF << endl;
for (int i = 1; i <= N; i++) {
if (F[i][i + N - 1][0] == maxF) {
cout << i << " ";
}
}
return 0;
}