动态规划——多边形游戏(求解最优解和最优解路径)

实验目的和要求

目的:利用动态规划实现多边形游戏

要求:给定一个多边形,顶点和边已进行标注。问:按照游戏规则,最高得分(最优值)是多少?对应最高得分,按照什么顺序移走边(最优解)?

规则:游戏第一步,将一条边删除。随后n-1步按照以下方式操作

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

解题思路

       ①由于多边形为环形,为方便处理,创建一个两倍于多边形顶点数的数组,用于存储多边形顶点。即val[i+n] = val[i],i<n。

       ②根据游戏规则,边的删除是不按照顺序进行的,即若需要求i号顶点到j号顶点的最高得分,则i与j之间必然存在一点k,使最高得分为i到k的最优值与k+1到j的最优值进行k号运算符的运算。转化为动态规划,状态转移方程:dp[i][j]=max{dp[i][j],count(dp[i][k],dp[k+1][j],sign[k])},count(int a,int b,char c)用于计算a与b按照c进行运算的最大值。

       ③动态规划初值问题,dp[i][i]表示i号顶点到i号顶点的最优值,即为val[i]。n个顶点的最优值可以通过一次求解1个,2个……n个顶点的情况来求解。

       ④关于最大值得求解问题,由于涉及到了加号与乘号的问题。

         对于加号来说,求两段顶点序列的计算最大值即为两侧最大值之和。

         对于乘号来说,由于考虑到最大值也有可能是负数乘以负数得到,但两段顶点序列的最大值必然在边缘取到。即max{最大*最小,最小*最小,最大*最大,最小*最大}所以需要另外维护一个数组用于存储每一步计算的最小值。最小值的求解类似于最大值。

       ⑤关于输出最优解的问题,由于使用动态规划要求无后向性与最优化原理,且该问题设计两个dp数组,无法在计算dp数组的同时保存最优解。因此考虑在计算dp完成后反向递归推导最优解。由于dp数组的每一行相当于对最开始删除某一条边的情况,则求出最优值后,对这一行进行遍历,当count(dp[i][k],dp[k+1][j],sign[k])等于最大值时,将sign[k]存入临时数组(为便于识别可以将k也存入另一临时数组),然后对i与k之间,k+1与j之间进行同样的操作,对应的最大值即为dp[i][k],dp[k+1][j]。递归运算完成后对数组倒序输出即可。

代码

#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);
	}
} 

结果

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值