动态规划之多边形游戏

目录

  1. 多边形游戏简介
  2. 举例以及详细分析
  3. 代码块
  4. 测试结果

多边形游戏简介

问题描述:
多边形游戏是一个单人玩的游戏,开始时有一个由n个顶点构成的多边形。每个顶点被赋予一个整数值,每条边被赋予一个运算符“+”或“*”。所有边依次用整数从1到n编号。

  游戏第1步,将一条边删除。

  随后n-1步按以下方式操作:

  (1)选择一条边E以及由E连接着的2个顶点V1和V2;

  (2)用一个新的顶点取代边E以及由E连接着的2个顶点V1和V2。将由顶点V1和V2的整数值通过边E上的运算得到的结果赋予新顶点。

  最后,所有边都被删除,游戏结束。游戏的得分就是所剩顶点上的整数值。

  问题:对于给定的多边形,计算最高得分。

  1. 设所给定的多边形的顶点和边的顺时针序列位 op[1],v[1],op[2],v[2],….,op[n],v[n],其中,op[i]表示第i条边所对应的运算符,v[i]表示第i个顶点上的数值,i=1~n。
  2. 在所给多边形中,从顶点i(1<=i<=n)开始,长度为j的(链中有j个顶点)的顺时针链p(i,j)可以表示为:
    v[i],op[i+1,v[i+1],op[i+2],……,v[i+j-1];
  3. 如果这条链的最后一次合并运算在op[i+s]处发生(1<=s<=j-i),则可在op[i+s]处将链分为两个子链p(i,s)p(i+s,j-s);
  4. 设m1是对子链p(i,s)的任意一种合并方式得到的值,而a和b分别是在所有可能的合并中得到的最小值和最大值。m2是子链p(i+s,j-s)的任意一种合并方式得到的值,而c和d分别是在所有的可能的合并中得到的最小值和最大值。依次定义有a<=m1<=b,c<=m2<=d;
  5. 由于子链p(i,s)和p(i+s,j-s)的合并方式决定了p(i,j)在op[i+s](为符号+或*)处断开后的合并方式,在op[i+s]处合并后的值为m=(m1)opi+s;
    (1)当op[i+s]=’+’时,显然有a+c≤m≤b+d
    (2)当op[i+s]=’*’时,有min{ac,ad,bc,bd}≤m≤max{ac,ad,bc,bd}

举例以及详细分析

这里写图片描述
如给出的次多边形可以看成一条链:
这里写图片描述
题目要求是删除一条边,然后按照步骤求最后的最大值,有题可知删除哪一条边,哪一条边的符号在4个顶点的运算中就不会用到
由上面简介可知此题符合动态规划的最优子结构性质,因此由动态规划的递归求解可知:
这4个顶点的推算是由3个顶点而来的,而3个顶点的推算是由2个顶点推算来的,2个顶点的推算是由一个顶点推算而来的。

  1. 而连续的一个顶点的运算就是这个顶点本身
  2. 连续的两个顶点的运算只包含他们中间的一条边,即一个符号。
  3. 连续的三个顶点的运算类似于n个矩阵的乘积,符合乘法交换律,也就是所谓的合并运算处。
  4. 连续的4个顶点运算也符号乘法交换律,也有合并运算处。

    这里写图片描述

由图可得

  1. 删去第一条边的最大值即m[1][4][1]=33
  2. 删去第二条边的最大值即m[2][4][1]=33
  3. 删去第三条边的最大值即m[3][4][1]=7
  4. 删去第一条边的最大值即m[4][4][1]=6

代码块

#include<iostream>  
using namespace std;

#define MAX 1024  
char op[MAX];//记录边的符号+ , *  
int m[MAX][MAX][2];//记录m[i][j][0],m[i][j][1]  
int N;              //        (从i顶点开始的连续j(包括i)个顶点的的权值最小值和最大值)

void dealFunc(int n, int i, int j)//处理从第i节点开始的连续j个节点,求出m[i][j][0],m[i][j][1]  
{
    for (int k = 1; k <= j - 1; k++)//表示从i开始的连续的k个处断除
    {
        int a = m[i][k][0];  //假设求得时从i开始的j个权值最优,那么
        int b = m[i][k][1];   //就有j-1个选择;
        int next = i + k;//表示断开处
        if (next>N)//边界问题  
            next %= N;
        int c = m[next][j - k][0];//j-k表示从i开始的连续个k处断开之后
        int d = m[next][j - k][1];//还剩下j-k个没有计算
        int max, min;
        if (op[next] == '+')//+  
        {
            max = b + d;
            min = a + c;
        }
        else//* 这里就是为什么还要求m[i][j][0]的原因啦。  
        {
            int e[4];
            e[0] = a*c;
            e[1] = a*d;
            e[2] = b*d;
            e[3] = b*c;
            min = e[0];
            max = e[0];
            for (int i = 1; i<4; i++)//当op[i+s]="*"
            {                        //m[i,j,0]=min{ac,ad,bc,bd};
                if (min>e[i])        //m[i,j,1]=max{ac,ad,bc,bd};
                    min = e[i];
                if (max<e[i])
                    max = e[i];
            }

        }

        if (m[i][j][0]>min)    //由于最优断开位置s有1<=s<=j-1的j-1种情况由此可知
            m[i][j][0] = min;  //m[i,j,0]=min(min[i,j,s]);
        if (m[i][j][1]<max)    //m[i,j,1]=max(max[i,j,s]);
            m[i][j][1] = max;
    }
}

void main()
{
    cout << "请输入点的个数:";
    cin >> N;
    int i, j;
    int value;
    char edgeFlag;
    //整个游戏节点和边集合顺序为边1,节点1,边2,节点2.......边N,节点N  
    for (i = 1; i <= N; i++)//从第一个顶点开始到最后一个顶点
    {
        cout << "请输入点和边的值";
        cin >> edgeFlag >> value;
        m[i][1][0] = m[i][1][1] = value;//从每个顶点开始的连续一个顶点的权值最大就是顶点值本身
        for (j = 2; j <= N; j++)
        {
            m[i][j][0] = MAX;
            m[i][j][1] = MAX*(-1);
        }
        op[i] = edgeFlag;
    }
    for (j = 2; j <= N; j++)
    {
        for (int i = 1; i <= N; i++)
        {
            dealFunc(N, i, j);//先从每个顶点开始的连续1个
        }                     //然后从每个顶点开始的连续2个,之后以此类推
    }

    int max = m[1][N][1];
    int edge = 1;
    for (i = 1; i <= N; i++)
    {
        cout << "删除第" << i << "条边时为 " << m[i][N][1] << endl;
        if (m[i][N][1]>max)
        {
            max = m[i][N][1];
            edge = i;
        }
    }
    cout << "删除第" << edge << "时为最大:" << max << endl;
    system("pause");
}

测试结果

这里写图片描述

展开阅读全文

没有更多推荐了,返回首页