题意:多边形游戏是一个单人玩的游戏,开始时有一个由n个顶点构成的多边形。每个顶点被赋予一个整数值,每条边被赋予一个运算符“+”或“*”。所有边依次用整数从1到n编号
游戏第1步,将一条边删除
随后n-1步按以下方式操作
(1)选择一条边E以及由E连接着的2个顶点V1和V2
(2)用一个新的顶点取代边E以及由E连接着的2个顶点V1和V2。将由顶点V1和V2的整数值通过边E上的运算得到的结果赋予新顶点
最后,所有边都被删除,游戏结束。游戏的得分就是所剩顶点上的整数值;
游戏第1步,将一条边删除
随后n-1步按以下方式操作
(1)选择一条边E以及由E连接着的2个顶点V1和V2
(2)用一个新的顶点取代边E以及由E连接着的2个顶点V1和V2。将由顶点V1和V2的整数值通过边E上的运算得到的结果赋予新顶点
最后,所有边都被删除,游戏结束。游戏的得分就是所剩顶点上的整数值;
这道题很有意思,也比较有难度,是一个算式形式的动态规划,和区间dp还是有一定联系的,因为我们每次开始游戏时需要断开一条边,之后就变成了一条链,可以看做区间,这样,我们初步应该会想到f[l][r]来表示l到r合并后最大的价值。
!!但是,这样就会有一个非常谜的问题!最大值不仅是由最大值加和而来的,它还可能是由两个最小值乘来的(负负得正),这样题就没法做了,所以这个子问题不满足无后效性!只能另寻它法!虽然说这个子问题不合适,但是这离我们的正解也已经非常近了。
我们可以用f[l][r]表示l到r所能合成的最大值,然后f1[l][r]表示l到r能合成的最小值。为什么这就能满足无后效性呢,因为啊:
1.一个最大值无非就是由两个最大值相加,两个最大值相乘或者两个最小值相乘而得来的!
2.一个最小值无非就是由两个最小值相加,或两个最小值相乘,或前一个最小值和后一个最大值相乘,或后一个最小值和前一个最大值相乘而得来的!
所以,我们就能够依据上述关系写出状态转移方程,这里就不给出了;
这样呢,我们就能够用区间dp的套路安排循环!但是有一个巨大的问题,我们需要枚举首先断掉那条边!这样下来会很麻烦,怎么办呢。在处理动态规划的环形问题时,我们可以先把这个环断掉,从任意位置断掉,然后长度复制一条一模一样的链连接在其后,这样我们就可以通过区间的移动来达成首先断一条边的操作;因为区间每次向右移动一位,都代表着把原来断的那条边接上,在把现有的最左边这条边断掉!这样就能达成一个。枚举先断哪条边的作用!
于是这道题完美解决,下面看代码!
1 //怕你飞远去 2 //怕你离我而去 3 //更怕你永远停留在这里 4 #include<iostream> 5 #include<cstdio> 6 #include<cstdlib> 7 #include<cstring> 8 #include<string> 9 #include<cmath> 10 #include<algorithm> 11 #include<queue> 12 using namespace std; 13 const int MAXN=125; 14 int f[MAXN][MAXN],f1[MAXN][MAXN],a[MAXN],n;//f[l][r]代表区间l到r所能合成的最大值,f1[l][r]表示区间l到r所能合成的最小值; 15 char b[MAXN]; 16 int main() 17 { 18 scanf("%d",&n); 19 memset(f,-0x3f,sizeof(f));//初始化; 20 memset(f1,0x3f,sizeof(f1));//初始化; 21 for(int i=1;i<=n;i++){ 22 getchar(); 23 scanf("%c%d",&b[i],&a[i]); 24 b[i+n]=b[i];a[i+n]=a[i];//断开,复制成一个两倍长度的链; 25 } 26 for(int i=1;i<=2*n;i++){ 27 f1[i][i]=f[i][i]=a[i];//初始化; 28 }//区间Dp 29 for(int i=2;i<=n;i++){//阶段(区间的长度) 30 for(int l=1;l<=n*2-i+1;l++){//状态(左端点) 31 int r=l+i-1;//状态(右端点) 32 for(int k=l;k<r;k++){//决策(嗯) 33 if(b[k+1]=='t'){ 34 f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]);//最大值由两个最大值相加而来 35 f1[l][r]=min(f1[l][r],f1[l][k]+f1[k+1][r]);//最小值由两个最小值相加而来 36 } 37 else{ 38 f[l][r]=max(f[l][r],f[l][k]*f[k+1][r]);//最大值由两个最大值相乘而来 39 f[l][r]=max(f[l][r],f1[l][k]*f1[k+1][r]);//最大值由两个最小值相乘而来 40 f1[l][r]=min(f1[l][r],f1[l][k]*f1[k+1][r]);//最小值由两个最小值相乘而来 41 f1[l][r]=min(f1[l][r],f[l][k]*f1[k+1][r]);//最小值由前一个最大值和后一个最小值相乘而来 42 f1[l][r]=min(f1[l][r],f1[l][k]*f[k+1][r]);//最小值由前一个最小值和后一个最大值相乘而来; 43 } 44 } 45 } 46 } 47 int maxx=-0x7fffffff; 48 for(int i=1;i<=n;i++){ 49 maxx=max(maxx,f[i][i+n-1]); 50 } 51 printf("%d",maxx); 52 puts(""); 53 for(int i=1;i<=n;i++){ 54 if(f[i][n-1+i]==maxx){//枚举,寻找第一步最优策略,有几个输出几个! 55 printf("%d ",i); 56 } 57 } 58 puts(""); 59 return 0; 60 }