源码置于文末,取用请点赞
1、实验环境
Visual C++ 6.0
2、实验目的和要求
一、实验目标: 利用动态规划实现多边形游戏。
二、实验内容:
- 给定N个顶点的多边形,每个顶点标有一个整数,每条边上标有+(加)或是×(乘)号,并且N条边按照顺时针依次编号为1~N。
三、实验要求:
- 给定一个多边形,顶点和边已进行标注。问:按照游戏规则,最高得分(最优值)是多少?对应该最高得分,按照什么顺序移走边(最优解)?
3、解题思路、伪代码
3.1解题思路:
设所给的多边形的顶点和边的顺时针序列为:op[1],v[1],op[2],v[2],...,op[n],v[n],其中op[i]表示第i条边所对应的运算符,v[i]表示第i个顶点上的数值(1<=i<=n)。从顶点i(1<=i<=n)开始,长度为j(链中有j个顶点)的顺时针链p(i,j)可表示为:v[i],op[i+1],...,v[i+j-1]。
若这条链p(i,j)的最后一次合并运算在op[i+s](1<=s<=j-1)处,则在op[i+s]处将链分割为两个子链p(i,s)和p(i+s,j-s)。设m1是对子链p(i,s)的任意一种合并方式得到的值,a和b分别所有合并中的最小值和最大值;m2是p(i+s,j-1)的任意一种合并方式得到的值,c和d分别是所有合并中的最大值和最小值;故a<=m1<=b,c<=m2<=d。m=m1 op[i+s] m2是两条子链合并的值;当op[i+s]=’+’时,a+b<=m<=b+d;当op[i+s]=’*’时,由于v[i]可取负数,故min{ac,ad,bc,bd}<=m<=max{ac,ad,bc,bd};所以主链的最大和最小值可由子链的最大和最小值得到。
作为一个封闭的多边形,删除了第一条边以后,需要从每个边处断开一次分成2个子链,这里就一定要每个都遍历一遍之后才可以得到最后的最大值。而且本实验由于负数和乘号的存在,使得在求最大值时需要分情况讨论。如果操作符为+的话,只需要两个链的最大值相加即可,如果操作符是x的话,那么必须把各种情况考虑进来,然后再求出最大值。
3.2 伪代码
int N,m[NMAX+1][NMAX+1][2],v[NMAX+1];
char op[NMAX+1];
main()
{
int p;
print("请输入多边形顶点数:" );input(N);
for (int i = 1; i <= N; i++)
{
print("请输入多边形顶点");input(v[i]);
m[i][1][0] = v[i];
m[i][1][1] = v[i];
print("请输入多边形边");input(op[i]);
}
print("多边形游戏首次删除第"); PloyMax(N, p);
}
int PloyMax(int n, int& p)
{
int minf, maxf;
for (int j = 2; j <= n; j++) //迭代链的长度(所含顶点数目)
{
for (int i = 1; i <= n; i++)//迭代首次删掉第i条边
{
for (int s = 1; s <= j - 1; 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;
}
}
}
int temp = m[1][n][1];
p = 1;
for (int i = 2; i <= n; i++)
{
if (temp < m[i][n][1])
{
temp = m[i][n][1];
p = i;
}
}
return temp;
}
void MinMax(int n, int i, int s, int j, int &minf, int &maxf)
{
int e[5];
int a = m[i][s][0], b = m[i][s][1];
int r = (i + s - 1) % n + 1;//多边形的实际顶点编号
int c = m[r][j - s][0], d = m[r][j - s][1];
if (op[r - 1] == '+')
{
minf = a + c;
maxf = b + d;
}
else
{
e[1] = a * c;
e[2] = a * d;
e[3] = b * c;
e[4] = d * b;
minf = e[1];
maxf = e[1];
for (int r = 2; r < N; r++)
{
if (minf > e[r])minf = e[r];
if (maxf < e[r])maxf = e[r];
}
}
}
4、实验步骤
4.1输入:
4.2输出:
源码:
#include<stdio.h>
#define maxnum 100
#define Max 999999
#define Min -999999
int dp[maxnum*2][maxnum*2][2]; //dp[][][0]用于存储最大值,dp[][][1]用于存储最小值
int val[maxnum]; //存储顶点值
char sign[maxnum]; //存储边上符号
char order[maxnum]; //存储各种情况下的最优删除顺序
int ordord=0;
int ord[maxnum];
int countMax(int i,int k,int len,char c){ //用于计算最大值
int max = Min,temp;
if(c=='+'){
return dp[i][k][0]+dp[k+1][i+len][0];
}
if(c=='*'){
temp = dp[i][k][0]*dp[k+1][i+len][0];
if(temp>max){
max = temp;
}
temp = dp[i][k][0]*dp[k+1][i+len][1];
if(temp>max){
max = temp;
}
temp = dp[i][k][1]*dp[k+1][i+len][1];
if(temp>max){
max = temp;
}
temp = dp[i][k][1]*dp[k+1][i+len][0];
if(temp>max){
max = temp;
}
return max;
}
}
int countMin(int i,int k,int len,char c){ //用于计算最小值
int min = Max,temp;
if(c=='+'){
return dp[i][k][1]+dp[k+1][i+len][1];
}
if(c=='*'){
temp = dp[i][k][1]*dp[k+1][i+len][1];
if(temp<min){
min = temp;
}
temp = dp[i][k][0]*dp[k+1][i+len][0];
if(temp<min){
min = temp;
}
temp = dp[i][k][1]*dp[k+1][i+len][0];
if(temp<min){
min = temp;
}
temp = dp[i][k][0]*dp[k+1][i+len][1];
if(temp<min){
min = temp;
}
return min;
}
}
void getord(int maxline,int maxn,int n){
int flag=0;
int k;
int len = n-1;
if(maxline==maxline+n-1){
return;
}
else{
for(k=maxline;k<maxline+len;k++){
if(countMax(maxline,k,len,sign[k])==maxn){
order[ordord] = sign[k];
ord[ordord] = k;
ordord++;
break;
}
}
getord(maxline,dp[maxline][k][0],k-maxline+1);
getord(k+1,dp[k+1][maxline+n-1][0],maxline+n-1-k-1+1);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&val[i]);
scanf("%c",&sign[i]);
val[i+n] = val[i];
sign[i+n] = sign[i];
}
for(int i=0;i<2*n;i++){
dp[i][i][0] = val[i];
dp[i][i][1] = val[i];
}
for(int len=1;len<n;len++){
for(int i=0;i<n;i++){
int max=Min,min=Max,tempmax,tempmin;
for(int k=i;k<i+len;k++){
tempmax = countMax(i,k,len,sign[k]);
tempmin = countMin(i,k,len,sign[k]);
if(tempmax>max){
max = tempmax;
}
if(tempmin<min){
min = tempmin;
}
}
dp[i][i+len][0] = max;
dp[i][i+len][1] = min;
if(i+len+n<2*n){
dp[i+n][i+n+len][0] = dp[i][i+len][0];
dp[i+n][i+n+len][1] = dp[i][i+len][1];
}
}
}
int maxline,maxn = Min;
for(int i=0;i<n;i++){
if(dp[i][i+n-1][0]>maxn){
maxn = dp[i][i+n-1][0];
maxline = i;
}
}
if(maxline==0){
printf("最优值为%d,删除%d号边得到\n",maxn,maxline+n);
}
else
printf("最优值为%d,删除%d号边得到\n",maxn,maxline);
getord(maxline,maxn,n);
printf("删除边的顺序为:");
for(int i=n-2;i>=0;i--){
printf("%c(%d)",order[i],ord[i]%n+1);
}
}