题目链接
题目大意
现在有一个n边形,每个顶点有一个值,每一条边有一个操作(加或者乘),首先先删除一条边使其变成一条链状的,然后按照操作合并相邻的点,直到最后只剩一个点,求最后那个点的最大值和第一步删除那个边可以得出那个最大值
解题思路
神一般的负负得正… … …如果只找最大值的话,可能得不到最大值,可能两个负数相乘得到的值更大… …
其他的就和正常的区间dp是一样的,先枚举第一步去掉的是哪个边,然后区间dp得出去掉这个边得到的最大值保存一下排序输出就好啦
具体的看代码
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[105];
int e[105][105],e1[105][105];
int dpd[105][105],dpx[105][105],tot,n;//dpd保存最大值,dpx保存最小值
const int inf=0x3f3f3f3f;
struct node
{
int id,v;
};
node b[105];
int cmp(node x,node y)
{
if(x.v==y.v)
return x.id<y.id;
return x.v>y.v;
}
void F(int L,int R)
{
//printf("%d %d\n",L,R);
memset(e1,0,sizeof(e1));
for(int i=L,j=1; i<=R; i++,j++)
{
dpx[j][j]=dpd[j][j]=a[i];
e1[j][j+1]=e[i][i+1];
}
for(int l=1; l<n; l++)//枚举长度
{
for(int i=1; i<=n-l; i++)//枚举左端点
{
int j=i+l;//右端点
dpd[i][j]=-inf;
dpx[i][j]=inf;
for(int k=i; k<j; k++)
{
if(e1[k][k+1]==1)//加号
{
dpd[i][j]=max(dpd[i][j],dpd[i][k]+dpd[k+1][j]);
dpx[i][j]=min(dpx[i][j],dpx[i][k]+dpx[k+1][j]);
}
else//乘号
{
dpd[i][j]=max(dpd[i][j],dpd[i][k]*dpd[k+1][j]);
dpd[i][j]=max(dpd[i][j],dpx[i][k]*dpx[k+1][j]);
dpx[i][j]=min(dpx[i][j],dpx[i][k]*dpx[k+1][j]);
dpx[i][j]=min(dpx[i][j],dpx[i][k]*dpd[k+1][j]);
dpx[i][j]=min(dpx[i][j],dpd[i][k]*dpx[k+1][j]);
}
}
}
}
}
int main()
{
char s[5];
while(~scanf("%d",&n))
{
memset(e,0,sizeof(e));
for(int i=1; i<=n; i++)//先把它扩展成2*n
{
scanf("%s %d",s,&a[i]);
a[n+i]=a[i];
if(i>1)
{
e[i][i-1]=e[i-1][i]=s[0]=='t'?1:2;
e[n+i][n+i-1]=e[n+i-1][n+i]=s[0]=='t'?1:2;
}
else
e[n][n+1]=e[n+1][n]=s[0]=='t'?1:2;
}
for(int i=1; i<=n; i++)//枚举第一步断的是那条边
{
if(i==1)//计算最大值
F(n+1,2*n);
else//计算最大值
F(i,i+n-1);
b[i].id=i;
b[i].v=dpd[1][n];//记录最大值
}
sort(b+1,b+1+n,cmp);//排序
int ans=b[1].v;
printf("%d\n",ans);
printf("%d",b[1].id);
for(int i=2; i<=n; i++)
{
if(b[i].v==ans)
printf(" %d",b[i].id);
else
break;
}
printf("\n");
}
return 0;
}