/*
Name: 多边形游戏
Copyright:
Author: 巧若拙
Date: 28-03-17 15:39
Description:
问题描述:
多边形游戏是一个单人玩的游戏,开始时有一个由n个顶点构成的多边形。每个顶点被赋予一个整数值,
每条边被赋予一个运算符“+”或“*”。所有边依次用整数从1到n编号。
游戏第1步,将一条边删除。
随后n-1步按以下方式操作:
(1)选择一条边E以及由E连接着的2个顶点V1和V2;
(2)用一个新的顶点取代边E以及由E连接着的2个顶点V1和V2。将由顶点V1和V2的整数值通过边E上的运算得到的结果赋予新顶点。
最后,所有边都被删除,游戏结束。游戏的得分就是所剩顶点上的整数值。
问题:对于给定的多边形,计算最高得分。
问题求解:
当把一条边去除除后,再把它拉直,那么这个问题就可以变成一条链。
那么就和以前写的矩阵连乘问题和凸多边形最优三角剖分相似,
其实我们最后要求的是这个链的表达式算式结果的最大值。
于是我们就可以想到可以用数组p[i][j]来表示从点i开始,链长为j的算术表达式的最大值,用v[i]存储操作数,op[i]存储操作符。
如果这条链的最后一次合并运算在op[i+s]处发生(1≤s≤j-1),则可在op[i+s]处将链分割为2个子链p[i][s]和p[i+s][j-s]。
似乎这样再按照以前解决动态规划题目时的思路,就可以解决问题了。
但是,我们再来考虑一下,由于有两种运算符+和x,并且操作数可能存在负数,
那么我们也必须考虑两个负数相乘的结果可能比两个正数要打,所以我们同时还需要记录每个链的最大和最小值,然后判断,
如果操作符为+的话,只需要两个链的最大值相加即可,如果操作符是x的话,那么必须把各种情况考虑进来,然后再求出最大值。
分析如下:
设m1是对子链p[i][s]的任意一种合并方式得到的值,而a和b分别是在所有可能的合并中得到的最小值和最大值。
m2是p[i+s][j-s]的任意一种合并方式得到的值,而c和d分别是在所有可能的合并中得到的最小值和最大值。
依此定义有a<=m1<=b,c<=m2<=d
(1)当op[i+s]='+'时,显然有a+c<=m<=b+d
(2)当op[i+s]='*'时,有min{ac,ad,bc,bd}<=m<=max{ac,ad,bc,bd}
换句话说,主链的最大值和最小值可由子链的最大值和最小值得到。
于是我就用数组chain_value[i][j][2]来存储链的节点,
其中chain_value[i][j][0]表示链的最大值,chain_value[i][j][1]表示链的最小值。
*/
#include<iostream>
using namespace std;
int PloyMax(int i, int j, int n);//自顶向下,使用备忘录数组的动态规划算法
int PloyMax_2(int n);//自顶向下,使用备忘录数组的动态规划算法
void MinMax(int n, int i, int s, int j, int &minf, int &maxf);//求最大值和最小值
const int INF = -999999; //自定义的负无穷大
const int N = 6;
int v[N] = {1,3,-2,-3,5-6};
char op[N] = {'+', '*', '*','+', '*', '+'};
int m[N][N+1][2]; //m[i][j][0]和m[i][j][1]分别链p(i,j)的最小值和最大值
int main(int argc, char **argv)
{
for (int i=0; i<N; i++)//初始化
{
m[i][1][0] = m[i][1][1] = v[i];
}
cout << PloyMax_2(N) << endl; //自底向上的动态规划算法
for (int i=0; i<N; i++)//初始化
{
for (int j=2; j<=N; j++)
{
m[i][j][0] = m[i][j][1] = INF;
}
}
int maxf = INF;
for (int i=0; i<N; i++)
{
int f = PloyMax(i, N, N);//自顶向下,使用备忘录数组的动态规划算法
cout << "f=" << f << endl;
if (f > maxf)
maxf = f;
}
cout << maxf << endl;
system("pause");
return 0;
}
int PloyMax(int i, int j, int n)//自顶向下,使用备忘录数组的动态规划算法
{
int e[4] = {0};
int a, b, c, d, r, minf, maxf;
for (int s=1; s<j; s++)
{//先分别处理两个子问题
if (m[i][s][0] == INF || m[i][s][1] == INF)
PloyMax(i, s, n);
a = m[i][s][0], b = m[i][s][1];
r = (i+s) % n;
if (m[r][j-s][0] == INF || m[r][j-s][1] == INF)
PloyMax(r, j-s, n);
c = m[r][j-s][0], d = m[r][j-s][1];
// cout << "i= " << i << " j= " << j << " s = " << s << " r = " << r << " : " << a << " " << b << " " << c << " " << d << endl;
if (op[r] == '+')
{
minf = a + c;
maxf = b + d;
}
else
{
e[0] = a * c;
e[1] = a * d;
e[2] = b * c;
e[3] = b * d;
minf = maxf = e[0];
for (int k=1; k<4; k++)
{
if (e[k] < minf)
minf = e[k];
else if (e[k] > maxf)
maxf = e[k];
}
}
if (m[i][j][0] == INF || m[i][j][0] > minf)
m[i][j][0] = minf;
if (m[i][j][1] == INF || m[i][j][1] < maxf)
m[i][j][1] = maxf;
// cout << "m[" << i << "][" << j << "][0] = " << m[i][j][0] << " " << "m[" << i << "][" << j << "][1] = " << m[i][j][1] <<endl;
}
return m[i][j][1];
}
int PloyMax_2(int n)//自底向上的动态规划算法
{
int minf, maxf;
for (int j=2; j<=n; j++)
{
for (int i=0; i<n; i++)
{
for (int s=1; s<j; s++)
{
MinMax(n, i, s, j, minf, maxf);
if (m[i][j][0] > minf)
m[i][j][0] = minf;
if (m[i][j][1] < maxf)
m[i][j][1] = maxf;
}
}
}
maxf = m[0][n][1];
for (int i=1; i<n; i++)
{
if (m[i][n][1] > maxf)
maxf = m[i][n][1];
}
return maxf;
}
void MinMax(int n, int i, int s, int j, int &minf, int &maxf)//求最大值和最小值
{
int e[4] = {0};
int a = m[i][s][0], b = m[i][s][1];
int r = (i+s) % n;
int c = m[r][j-s][0], d = m[r][j-s][1];
if (op[r] == '+')
{
minf = a + c;
maxf = b + d;
}
else
{
e[0] = a * c;
e[1] = a * d;
e[2] = b * c;
e[3] = b * d;
minf = maxf = e[0];
for (int k=1; k<4; k++)
{
if (e[k] < minf)
minf = e[k];
else if (e[k] > maxf)
maxf = e[k];
}
}
}