题目链接:http://poj.org/problem?id=1179
问题:给定一个环,其中边上是操作,有加和乘两种,点上是数值,执行的操作如下:找一条边断开,然后任选边开始将边相连的两个点进行运算后变成新的点,计算到最后变成一个点时这个点的最大值。由于合并两个区间的时候,如果操作数是加法的话容易得到和区间的最大值和最小值,如果是乘法的话仅仅知道区间的最大值是不行的,因为可能有两个很小的负数相乘得到的值才是最大值,所以考虑将区间的最小值和最大值信息都进行保存,那么和区间的最小值和最大值一定在两个区间的这两个属性的两两组合的乘积中的最小的一个活着最大的一个。
唯一的注意点是边的给出是在点之前的,所以k与k+1编号的数进行的运算是使用的第k+1个操作数。
代码:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 110,inf=10000000; int w[maxn]; char c[maxn]; int f[maxn][maxn],g[maxn][maxn]; int n; int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>c[i]>>w[i]; c[n+i]=c[i]; w[n+i]=w[i]; } memset(f,-0x3f,sizeof(f)); memset(g,0x3f,sizeof(g)); for(int len=1;len<=n;len++) for(int i=1;i+len-1<=2*n;i++) { int j=i+len-1; if(len==1) f[i][j]=g[i][j]=w[i]; else{ for(int k=i;k<j;k++){ int maxl=f[i][k],minl=g[i][k]; int maxr=f[k+1][j],minr=g[k+1][j]; char op=c[k+1]; if(op=='t'){ f[i][j]=max(f[i][j],maxl+maxr); g[i][j]=min(g[i][j],minl+minr); }else{ int x1=minl*minr,x2=minl*maxr; int x3=maxl*maxr,x4=maxl*minr; f[i][j]=max(f[i][j],max(max(x1,x2),max(x3,x4))); g[i][j]=min(g[i][j],min(min(x1,x2),min(x3,x4))); } } } } int ans=-inf; for(int i=1;i<=n;i++) ans=max(ans,f[i][i+n-1]); cout<<ans<<endl; for(int i=1;i<=n;i++) if(f[i][i+n-1]==ans)cout<<i<<" "; return 0; }