Acwing 283.多边形 (区间DP)

题面

“多边形游戏”是一款单人益智游戏。

游戏开始时,给定玩家一个具有N个顶点N条边(编号1-N)的多边形,如图1所示,其中N = 4。

每个顶点上写有一个整数,每个边上标有一个运算符+(加号)或运算符*(乘号)。

第一步,玩家选择一条边,将它删除。

接下来在进行N-1步,在每一步中,玩家选择一条边,把这条边以及该边连接的两个顶点用一个新的顶点代替,新顶点上的整数值等于删去的两个顶点上的数按照删去的边上标有的符号进行计算得到的结果。

最终,游戏仅剩一个顶点,顶点上的数值就是玩家的得分,上图玩家得分为0。

请计算对于给定的N边形,玩家最高能获得多少分,以及第一步有哪些策略可以使玩家获得最高得分。

输入格式
输入包含两行,第一行为整数N。

第二行用来描述多边形所有边上的符号以及所有顶点上的整数,从编号为1的边开始,边、点、边…按顺序描述。

其中描述顶点即为输入顶点上的整数,描述边即为输入边上的符号,其中加号用“t”表示,乘号用“x”表示。

输出格式
输出包含两行,第一行输出最高分数。

在第二行,将满足得到最高分数的情况下,所有的可以在第一步删除的边的编号从小到大输出,数据之间用空格隔开。

数据范围
3≤N≤50,
数据保证无论玩家如何操作,顶点上的数值均在[-32768,32767]之内。

输入样例:
4
t -7 t 4 x 2 x 5
输出样例:
33
1 2

思路

我们很容易注意到它是一道区间dp,因为它和石子合并真的很像,但我们可以看出这里的难点在于这是一个环,我们要首先割去一条边,才会有一个比较明显的区间dp模型出现,第二个难点,我们在取值的时候,因为乘法的特殊性,我们无法保证两边都取最大值的时候,我得到的一定是最大值。关于第一点的话,我们这里有一个在区间dp和环形问题里面常用的一个技巧:破环成链,我们从一开始把这个环展开,然后在末尾端接上一个一样的展开链,那么这样我们就可以在这一段区间里面取到我们想要的区间了,而不用考虑如何割边的问题。第二个点的话,我们可以多加一个dp数组维护最小值,然后在状态转移的时候对左右区间穷尽所有可能,取其中的最大最小值更新就可以了。剩下的就是区间dp的框架了,枚举区间长度和起点。

代码实现

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define MT(x,i) memset(x,i,sizeof(x) )
#define rev(i,start,end) for (int i=0;i<end;i++)
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
#define MOD 1000000007
#define exp 1e-8
#define N 1000005 
#define fi first 
#define se second
#define pb push_back
typedef long long ll;
typedef pair<int ,int> PII;
typedef pair<int ,PII> PIII;
ll gcd (ll a,ll b) {return b?gcd (b,a%b):a; }
inline int read() {
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') {
        if(ch=='-') f = -1;
        ch=getchar();
    } 
    while('0'<=ch&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }   return x*f;
}

const int maxn=101;
int f[maxn][maxn];
int g[maxn][maxn];
int w[maxn];
char c[maxn];
int n;

int main () {
     cin>>n;
     rep (i,1,n) {
         char x;
         int y;
         cin>>x>>y;
         w[i]=w[i+n]=y;
         c[i]=c[i+n]=x;
     }
     rep (len,1,n) {
         for (int l=1;l+len-1<2*n;l++) {
             int r=l+len-1;
             if (l==r) f[l][r]=w[l],g[l][r]=w[l];
             else f[l][r]=-inf,g[l][r]=inf;
             rep (k,l,r-1) {
                char op=c[k+1];
                if (op=='t') {
                    int a=g[l][k],b=g[k+1][r],a2=f[l][k],b2=f[k+1][r];
                    f[l][r]=max (a2+b2,f[l][r]);
                    g[l][r]=min (a+b,g[l][r]);
                }
                else {
                    int a1=(f[l][k]*f[k+1][r]);
                    int b1=(f[l][k]*g[k+1][r]);
                    int c1=(g[l][k]*f[k+1][r]);
                    int d1=(g[l][k]*f[k+1][r]);
                    int maxnum=max (max (a1,b1),max (c1,d1));
                    int minnum=min (min (a1,b1),min (c1,d1));
                    f[l][r]=max (f[l][r],maxnum);
                    g[l][r]=min (g[l][r],minnum);
                }
             }
         }
     }
    int ans=-inf;
    rep (i,1,n) {
      ans=max (ans,f[i][i+n-1]);
    }
    cout<<ans<<endl;
    rep (i,1,n) 
        if (ans==f[i][i+n-1])  cout<<i<<" ";
        
    return 0; 
}
原文地址:https://www.cnblogs.com/hhlya/p/13436553.html