每次答案肯定是由两段经过符号合并而成,最终答案也是如此
maxl左半边最大值, maxr右半边最大值, minl左半边最小值, minr右半边最小值
类似于矩阵连乘时候的切割点,需要找出每次切掉的边
+号:加号时候的最大值一定是maxl + maxr 而最小值一定是minl + minr
*号:乘号有所不同,因为存在负数,所以最大值和最小值不能简单定义为maxl + maxr 和 minl + minr
最大值 = max(maxl * maxr, maxl * minr, minl * maxr, minl * minr)
最小值 = min(maxl * maxr, maxl * minr, minl * maxr, minl * minr)
要注意断环成链,”选择一个位置断开,复制成为长度为2倍的链“,可以解决环的问题
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int M = 110;
const int INF = 0x3f3f3f3f;
char sign[M];
int num[M];
int min1[M][M], max1[M][M];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
getchar();
scanf("%c%d", &sign[i], &num[i]);
sign[i + n] = sign[i];
num[i + n] = num[i];
}
memset(min1, INF, sizeof(min1));
memset(max1, -INF, sizeof(max1));
for (int len = 1; len <= n; ++len) {
for (int l = 1; l + len - 1 <= 2 * n; ++l) {
int r = l + len - 1;
if (len == 1) min1[l][r] = max1[l][r] = num[l];
else {
for (int k = l; k < r; ++k) {
char op = sign[k + 1];
if (op == 't') {
max1[l][r] = max(max1[l][r], max1[l][k] + max1[k + 1][r]);
min1[l][r] = min(min1[l][r], min1[l][k] + min1[k + 1][r]);
}
else {
int maxl = max1[l][k], maxr = max1[k + 1][r], minl = min1[l][k], minr = min1[k + 1][r];
max1[l][r] = max(max1[l][r], max(maxl * maxr, max(maxl * minr, max(minl * maxr, minl * minr))));
min1[l][r] = min(min1[l][r], min(maxl * maxr, min(maxl * minr, min(minl * maxr, minl * minr))));
}
}
}
}
}
int ans = -INF;
for (int i = 1; i <= n; ++i) ans = max(ans, max1[i][i + n - 1]);
printf("%d\n", ans);
bool flag = 0;
for (int i = 1; i <= n; ++i) {
if (ans == max1[i][i + n - 1] && flag == 0) {
printf("%d", i);
flag = 1;
continue;
}
if (ans == max1[i][i + n - 1] && flag == 1) {
printf(" %d", i);
}
}
return 0;
}