多边形游戏(经典的环形dp,区间DP)

【描述 】
一个多边形,开始有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);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪的期许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值