问题描述:
多边形游戏是一个单人玩的游戏,开始时有一个由n个顶点构成的多边形。每个顶点被赋予一个整数值,每条边被赋予一个运算符“+”或“*”。所有边依次用整数从1到n编号。
游戏第1步,将一条边删除。
随后n-1步按以下方式操作:
(1)选择一条边E以及由E连接着的2个顶点V1和V2;
(2)用一个新的顶点取代边E以及由E连接着的2个顶点V1和V2。将由顶点V1和V2的整数值通过边E上的运算得到的结果赋予新顶点。
最后,所有边都被删除,游戏结束。游戏的得分就是所剩顶点上的整数值。
问题:对于给定的多边形,计算最高得分。看了很多文章, 都没有用备忘录算法解决n后的问题,所以博主试着用备忘录基本思想撸了一个n后问题的备忘录版本。首先,简单说说什么是备忘录算法。备忘录方法是动态规划的变形。与动态规划算法一样,备忘录方法用表格保存已解决的子问题的答案,在下次需要解决此问题时,只要简单地查看该子问题的解答,而不必重新计算,从而减少算法的时间复杂度。与动态规划不同的是,备忘录方法的递归方式是自顶向下的,不同于动态规划的自底向上。
下面开始分析算法:
数据结构:
v[N+1] 存放顶点数值
op[N+1] 存放操作符
flag[N+1][N+1] 表示该段数据有没有写入,写入为1,否则为0
m[i][j] 表示该段的数据,i为起点,j为终点
place 限制返回范围
#include <iostream>
using namespace std;
#define N 100
int v[N + 1];
int m[N + 1][N + 1];
char flag[N + 1][N + 1];
char op[N + 1];
int p;
int n;
int maxf;
int place = 1;
int fun(int i, int j, int k)
{
if (op[j] == '+')
return i + k;
else return i*k;
}
int polyMax(int i, int j)
{
if (i == j)
{
m[i][j] = v[i];
return v[i];
}
if (flag[i][j] == 1&&i >= place&&j >= place)
{
return m[i][j];
}
int u = fun(polyMax(i, i), i, polyMax(i + 1, j));
for (int k = i + 1; k < j; k++)
{
int t = fun(polyMax(i, k), k, polyMax(k + 1, j));
if (t>u)
u = t;
}
m[i][j] = u;
flag[i][j] = 1;
return u;
}
void init()
{
for (int i = 1; i <= 2 * n - 1; i++)
for (int j = 1; j <= 2 * n - 1; j++)
{
m[i][j] = 0;
flag[i][j] = 0;
}
}
int main()
{
int temp;
cout << "请输入多边形顶点数:" << endl;
cin >> n;
for (int i = 1; i <= n; i++)
{
cout << "请输入多边形顶点" << i << "数值:" << endl;
cin >> v[i];
cout << "请输入多边形边" << i << "运算符:" << endl;
cin >> op[i];
}
maxf = polyMax(1, n);
p = 1;
for (int a = 2; a <= n; a++)
{
place++;
v[n + a - 1] = v[a-1];
v[a - 1] = 0;
op[n + a - 1] = op[a - 1];
temp = polyMax(a, n + a-1);
if (temp > maxf)
{
maxf = temp;
p = a;
}
}
cout <<"首次删掉第"<<p<<"条边,结果最大:"<< maxf << endl;
system("pause");
return 0;
}
代码具体分析:
因为多边形游戏起点是不定的,现假设第一次删的是第4条边。
对于上图,我们要求的是m[1][4]的值
不难理解:m[1][4]=max{m[1][1]op[1]m[2][4],m[1][2]op[2]m[3][4],m[1][3]op[3]m[4][4]}
而m[1][1]的值就是一个点,就是它本身的值1
m[2][4]=max{m[2][2]op[2]m[3][4],m[2][3]op[3]m[4][4]}
同理m[3][4],m[1][3]的值也可以求得。
我们可以用一个表格来表示:
可以发现上层的值可以通过下层的值得到。最后得到的结果24是第一条边删去的是op[4]
而根据问题我们需要每个边都要算一遍,这时候只需要把顶点的数据处理一下就可以了
可以用v[5]来表示v[1]的值,op[5]=op[1],然后从2~5开始再算一遍,这样就可以算出第一次删掉的边是op[1]的情况
n条边执行n次循环再筛选出其中的最大值,这就是问题的解。
执行效果如图: