\(IOI\) ?? 一道大水题
题面难以概括,还是去看原题吧
solution
环上 \(dp\) ,断环成链
然后做区间 \(dp\) 板子,枚举断点
这个题唯一的 \(tip\) 就是处理区间合并
因为区间合并有乘,所以有负负得正情况,所以还要维护一个区间最小值
对于合并要考虑一大堆情况,详细看这 题解 P4342 [IOI1998]Polygon
最后可以合并得到两个转移式 (\(g[i][j]\) 为区间最小值,\(f[i][j]\) 为区间最大值)
\(f[i][j]=max(f[i][j],max(f[i][k]×f[k+1][j],max(g[i][k]×g[k+1][j],max(f[i][k]×g[k+1][j],g[i][k]×f[k+1][j]))))\)
\(g[i][j]=min(g[i][j],min(f[i][k]×f[k+1][j],min(g[i][k]×g[k+1][j],min(f[i][k]×g[k+1][j],g[i][k]×f[k+1][j]))))\)
加的合并
\(f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j]);\)
\(g[i][j] = min(g[i][j], g[i][k] + g[k + 1][j]);\)
注意代码输入格式!!
\(code\)
/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N = 150;
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int a[N], n, f[N][N], g[N][N];
char c[N];
int main(){
scanf("%d\n", &n);
for (int i = 1; i <= n; i++) {
scanf("%c %d", &c[i], &a[i]); getchar();
c[i + n] = c[i], a[i + n] = a[i];//断环成链
}
memset(f, -0x3f3f3f3f, sizeof f), memset(g, 0x3f3f3f3f, sizeof g);
for (int i = 1; i <= (n << 1); i++) f[i][i] = g[i][i] = a[i];
for (int len = 1; len < n; len++)
for (int i = 1, j = i + len; j <= (n << 1); j++, i++) {
for (int k = i; k < j; k++) {
if (c[k + 1] == 'x') {
f[i][j] = max(f[i][j], max(f[i][k] * f[k + 1][j], max(g[i][k] * g[k + 1][j], max(f[i][k] * g[k + 1][j], g[i][k] * f[k + 1][j]))));
g[i][j] = min(g[i][j], min(f[i][k] * f[k + 1][j], min(g[i][k] * g[k + 1][j], min(f[i][k] * g[k + 1][j], g[i][k] * f[k + 1][j]))));
}
if (c[k + 1] == 't') {
f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j]);
g[i][j] = min(g[i][j], g[i][k] + g[k + 1][j]);
}
}
}
int Ans = -0x3f3f3f3f;
for (int i = 1; i <= n; i++) Ans = max(Ans, f[i][i + n - 1]);
printf("%d\n", Ans);
for (int i = 1;i <= n; i++) if (f[i][i + n - 1] == Ans) printf("%d ",i);
puts("");
return 0;
}