题目大意:有一个n边形,每条边有一个运算符号,每个点上有一个权值,一开始可以选择一条边删除,以后每次选择一条边将一条边的两个点合成一个点,这个点的权值为这两个点的权值与边的操作运算的结果。最后只有一个点,输出这个点的最大分值,以及删除哪些边可以得到这个分值。
分析:可以枚举删除哪条边,然后进行处理。
当 nnn = 222 的时候,最大值只能是这条边的两点运算。当n>2n > 2n>2时,最后的答案和删除边的顺序有一定关系。最后删除的那一条边的左右两端是两个独立的子问题,删除顺序互相不影响。而删除这条边得到的最大分值具有最优子问题结构:显然加法时两端尽量大答案最大,乘法较为复杂要分析两端最小的情况,因为存在负数结果,负数乘负数得正数。
这是一个类似石子合并的过程,但是它成环了,对于环可以将环断开,拷贝补成一个 2n2 n2n 长的链。
(尝试直接在环上DP感觉特别难写,放弃)
当拆成链的时候,复杂度下降了一维,变成了 n3n ^ 3n3,因为[i,i+n−1][i,i + n -1][i,i+n−1]的答案相当于删除了第 (imod  n)(i \mod n)(imodn) 个点和第 ((i+n−1)mod  n)((i + n - 1) \mod n)((i+n−1)modn)个点之间的边
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m;
const int maxn = 1e2 + 10;
int dp[maxn][maxn][2];
int a[maxn][maxn];
char s[2];
int tmp[maxn];
int sta[maxn];
int ans[maxn],res,cnt;
int vis[maxn];
int main() {
scanf("%d",&n);
for(int i = 0; i < 2 * n; i++) {
if(i % 2 == 0) {
scanf("%s",s);
tmp[i] = s[0];
}
else {
scanf("%d",&tmp[i]);
sta[i / 2] = tmp[i];
}
}
for(int i = 1; i < 2 * n; i += 2) {
int u = i, v = (i + 2) % (2 * n),p = (i + 1) % (2 * n);
a[u / 2][v / 2] = tmp[p];
a[v / 2][u / 2] = tmp[p];
}
//环处理,扩展成一条链
for(int i = 0; i < n; i++) {
sta[i + n] = sta[i];
a[i + n - 1][i + n] = a[(i + n - 1) % n][i];
a[i + n][i + n - 1] = a[i + n - 1][i + n];
dp[i][i][0] = dp[i][i][1] = dp[i + n][i + n][0] = dp[i + n][i + n][1] = sta[i];
}
n *= 2;
res = -0x3f3f3f3f;
for(int s = 2; s <= n / 2; s++) {
for(int l = 0; l + s - 1 < n; l++) {
int r = l + s - 1;
dp[l][r][0] = -0x3f3f3f3f;
dp[l][r][1] = 0x3f3f3f3f;
for(int k = l; k < r; k++) {
if(a[k][k + 1] == 't') {
dp[l][r][0] = max(dp[l][r][0],dp[l][k][0] + dp[k + 1][r][0]);
dp[l][r][1] = min(dp[l][r][1],dp[l][k][1] + dp[k + 1][r][1]);
}
else {
dp[l][r][0] = max(dp[l][r][0],dp[l][k][0] * dp[k + 1][r][0]);
dp[l][r][0] = max(dp[l][r][0],dp[l][k][1] * dp[k + 1][r][1]);
dp[l][r][0] = max(dp[l][r][0],dp[l][k][1] * dp[k + 1][r][0]);
dp[l][r][0] = max(dp[l][r][0],dp[l][k][0] * dp[k + 1][r][1]);
dp[l][r][1] = min(dp[l][r][1],dp[l][k][1] * dp[k + 1][r][0]);
dp[l][r][1] = min(dp[l][r][1],dp[l][k][0] * dp[k + 1][r][1]);
dp[l][r][1] = min(dp[l][r][1],dp[l][k][1] * dp[k + 1][r][1]);
dp[l][r][1] = min(dp[l][r][1],dp[l][k][0] * dp[k + 1][r][0]);
}
}
if(s == n / 2) {
if(res == dp[l][r][0]) {
if(vis[l % (n / 2) + 1]) continue;
vis[l % (n / 2) + 1] = 1;
ans[++cnt] = l % (n / 2) + 1;
}
else if(res < dp[l][r][0]) {
cnt = 0;
memset(vis,0,sizeof vis);
ans[++cnt] = l % (n / 2) + 1;
res = dp[l][r][0];
vis[l % (n / 2) + 1] = 1;
}
}
}
}
printf("%d\n",res);
for(int i = 1; i <= cnt; i++) {
if(i - 1) printf(" ");
printf("%d",ans[i]);
}
return 0;
}