算法实验:多边形游戏问题

 源码置于文末,取用请点赞

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

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

起剑倒悬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值