【描述 】
一个多边形,开始有n个顶点。每个顶点被赋予一个正整数值,每条边被赋予一个运算符“+”或“*”。所有边依次用整数从1到n编号。
现在来玩一个游戏,该游戏共有n步:
第1步,选择一条边,将其删除
随后n-1步,每一步都按以下方式操作:(1)选择一条边E以及由E连接着的2个顶点v1和v2; (2)用一个新的顶点取代边E以及由E连接着的2个顶点v1和v2,将顶点v1和v2的整数值通过边E上的运算得到的结果值赋给新顶点。
最后,所有边都被删除,只剩一个顶点,游戏结束。游戏得分就是所剩顶点上的整数值。那么这个整数值最大为多少?
【输入 】
第一行为多边形的顶点数n(n ≤ 50),其后有n行,每行为一个整数和一个字符,整数为顶点上的正整数值,字符为该顶点到下一个顶点间连边上的运算符“+”或“*”(最后一个字符为最后一个顶点到第一个顶点间连边上的运算符)。
【输出】
输出仅一个整数,即游戏所计算出的最大值。
【样例输入】
5
5+
9*
13*
-7*
-25+
【样例输出】
31850
【分析】:
顶点存在数组v中,边存在数组e中。
构造状态,用 f[i][j] 来代表以顶点i开头,长度(即包含的顶点数)为j时的最优值。
当选定开头顶点 i 后,就默认 i 前边的那条边被删除了(因为用不到它了);
但是此题有可能顶点是负数,那就存在负负相乘得正的情况,需同时维护最大值和最小值。
故: f[i][j][0]为以i开头,长度j时的最小值
f[i][j][1]为以i开头,长度j时的最大值
那么当前状态就可以由长度小于j的子状态递推而来,与矩阵连乘类似。
总体时间复杂度O(n^3)
枚举长度类似枚举区间,因此该解法也可以叫区间dp
【代码】:
#include<bits/stdc++.h>
using namespace std;
const int MAX=1010;
int v[MAX],N;//点和点数
char e[MAX];//边 * +
int f[MAX][MAX][2];
int main()
{
scanf("%d",&N);
for(int i=0;i<N;i++)
scanf("%d%c",&v[i],&e[i]);//不要输入空格
for(int i=0;i<N;i++)
f[i][1][0]=f[i][1][1]=v[i];//长度为1时
for(int j=2;j<=N;j++)//枚举长度
for(int i=0;i<N;i++)//枚举开头
for(int k=1;k<j;k++)//枚举左半部分子状态长度
{//因为多边形为环形,右半边子状态的开头越界时要模除N
int a=f[i][k][0],b=f[(i+k)%N][j-k][0];//min
int c=f[i][k][1],d=f[(i+k)%N][j-k][1];//max
if(e[i+k-1]=='+')
{
f[i][j][0]=min(f[i][j][0],a+b);
f[i][j][1]=max(f[i][j][1],c+d);
}
if(e[i+k-1]=='*')
{
int s[4]={a*b,a*d,c*b,c*d};
sort(s,s+4);
f[i][j][0]=s[0];
f[i][j][1]=s[3];
}
}
int ans=0;
for(int i=0;i<N;i++)
ans=max(ans,f[i][N][1]);
printf("%d\n",ans);
}