POJ1179 Polygon 【例题精讲】

    题意:多边形游戏是一个单人玩的游戏,开始时有一个由n个顶点构成的多边形。每个顶点被赋予一个整数值,每条边被赋予一个运算符“+”或“*”。所有边依次用整数从1到n编号
游戏第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 }
View Code

 

posted @ 2018-04-05 16:54  杜宇一声  阅读(648)  评论(1编辑  收藏  举报