题意:给定一个n个顶点n条边的多边形,每个顶点上有一个整数,每条边上有一个乘号或加号,第一次任意删去一条边,之后每次合并两个顶点为一个顶点,求只剩最后一个顶点是的最大值及有多少种方法得到这个最大值.
分析:有一个环,先删去一条边后变成一条链,每次合并两个顶点,要获得最大值,那就用区间DP好了.对于第一个操作"任意删去一条边"很好处理,直接断环为链,然后把链倍长.
然后就是区间DP的模板了,先枚举区间长度len,然后枚举左端点i,再用len和i表示出右端点j,最后枚举区间断点k.
至于状态转移,我们一个一个来分析.首先肯定要分乘号和加号两种情况讨论,然后再想想对于一次乘法运算要得到最大值,有两种情况,一是两个很大的正数相乘,而是两个很小的负数相乘,所以我们需要开两个数组,\(f1[i][j]\)表示合并区间\([i,j]\)得到的最大值,\(f2[i][j]\)表示合并区间\([i,j]\)得到的最小值.两个数组都要维护.
对于乘法运算,要得到最大值正如上段所述,
\(f1[i][j]=max(f1[i][j],max(f1[i][k]*f1[k+1][j],f2[i][k]*f2[k+1][j]))\)
要得到最小值,干脆把所有情况取min好了,
\(f2[i][j]=min(f2[i][j],min(f2[i][k]*f2[k+1][j],min(f1[i][k]*f1[k+1][j],min(f1[i][k]*f2[k+1][j],f2[i][k]*f1[k+1][j]))))\)
对于加法运算,就很容易了,
\(f1[i][j]=max(f1[i][j],f1[i][k]+f1[k+1][j])\)
\(f2[i][j]=min(f2[i][j],f2[i][k]+f2[k+1][j])\)
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
int a[105],f1[150][150],f2[150][150];
char ch[105];
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>ch[i]>>a[i];
a[n+i]=a[i];ch[n+i]=ch[i];
}
for(int i=1;i<=n*2;i++)
f1[i][i]=f2[i][i]=a[i];
for(int len=2;len<=n;len++){
for(int i=1;i<=2*n-len+1;i++){
int j=i+len-1;f1[i][j]=-1e9,f2[i][j]=1e9;
for(int k=i;k<j;k++){
if(ch[k+1]=='x'){
f1[i][j]=max(f1[i][j],max(f1[i][k]*f1[k+1][j],f2[i][k]*f2[k+1][j]));
f2[i][j]=min(f2[i][j],min(f2[i][k]*f2[k+1][j],min(f1[i][k]*f1[k+1][j],min(f1[i][k]*f2[k+1][j],f2[i][k]*f1[k+1][j]))));
}
else if(ch[k+1]=='t'){
f1[i][j]=max(f1[i][j],f1[i][k]+f1[k+1][j]);
f2[i][j]=min(f2[i][j],f2[i][k]+f2[k+1][j]);
}
}
}
}
int ans=-1e9;
for(int i=1;i<=n;i++)
ans=max(ans,f1[i][i+n-1]);
printf("%d\n",ans);
for(int i=1;i<=n;i++)
if(f1[i][i+n-1]==ans)printf("%d ",i);
return 0;
}