多边形游戏 动态规划C语言实现

问题描述

在这里插入图片描述
示例流程

思路

  刚拿到题目的时候感觉是一脸懵的,道理我都懂,但是思路和头绪倒是一直没有蹦出来,妥妥的一个模拟类题的茫然脸。
  第一个思路肯定是暴力穷举,当然这种办法比较low,而且很可能在时空复杂度上爆掉,所以直接扔掉。
  首先尝试手动模拟流程,寻找规律,主要从 + * 的顺序入手,观察数字对它们的影响。由于负数的存在,分类讨论难度过于繁复,先放一边,其他思路不可以再用它推进。
  因为是个最优解问题,很自然地想到局部最优推得全局最优,因此萌生了动态规划计算最优子结构的办法。

动态规划

  那么问题来了,既然是动态规划计算最优子结构,那怎样算最优子结构呢?不同于其他问题直接取最大值为最优子结构,因为这个问题存在乘法负负得正的情况,因此我们还需要考虑取添加分类讨论过程,将乘法时左右两边都为负列入考虑范围。
  考虑了最优子结构的定义,剩下的就是递归求解的过程了。我们知道动态规划中有两个非常重要的概念——状态转移方程和临界条件。我们从合并的角度考虑问题:
  +:
    min_now = minl + minr
    max_now = maxl + maxr
  *:
    min_now = min(minl * minr, maxl * maxr, minl * maxr, minr * maxl)
    max_now = max(minl * minr, maxl * maxr, minl * maxr, minr * maxl)
  式中,minl,maxl为合并前符号左边的最小值与最大值;minr,maxr为合并前符号右边的最小值与最大值。因为考虑到正负号的问题,所以*部分需要对四种情况讨论。
  个人认为这个思路还是穷举的味道比较重的。

代码

#include<bits/stdc++.h>
using namespace std;
int get_minmax(int left, int point, int len);

int n;
// 用了递归,数据不可能太大的,因为缓冲区会炸 
// max[i][j]从第i位开始,往后数j个数(包括第i个数),j是链子的长度 
int max_num[1000][1000], min_num[1000][1000];
char op[1000];

int get_minmax(int left, int point, int len){
	int min_now, max_now;				// 断在s时合起来的最大最小 
	int real_point = (left+point)%n;		// 因为是个圆,这意味这断real_point边 
	int minleft = min_num[left][point];		// 因为point<len,所以这里的区间都是被计算过的 
	int maxleft = max_num[left][point];
	int minright = min_num[real_point][len-point];
	int maxright = max_num[real_point][len-point];
	// 计算s点合并掉后新的点的最大值最小值 
	if(op[real_point] == '+'){
		min_now = minleft+minright;
		max_now = maxleft+maxright; 
	}else{
		min_now = min(minleft*minright, min(minleft*maxright, min(maxleft*minright, maxleft*maxright)));
		max_now = max(minleft*minright, max(minleft*maxright, max(maxleft*minright, maxleft*maxright)));
	}
	return min_now, max_now;
} 

int main(){
	scanf("%d", &n);
	char jump;
	scanf("%c", &jump); 
	for (int i = 0; i<n; i++){
		scanf("%c", &op[i]);
		int temp;
		scanf("%d", &temp);
		max_num[i][1] = temp;	// 初始化 
		min_num[i][1] = temp;
	}
	for (int i = 0;i<n;i++){
		printf("%d ",max_num[i][1]);
	}
	printf("\n");
	int min_now,max_now;			// 代表[i,j]中能出的最大值最小值 
	// 第一重对应不同长度下两边的最优解。因此到了n就是最终结果
	for(int j=2; j<=n; j++)			// 因为j=1已经在之前确定完了,长度为1的时候就是自己。
		// 第二重对应不同起点下不同长度的最优解,就是区间[i,i+j-1]的里的最优解
    	for(int i=0;i<n;i++)		// 要干掉第i个点  
    		// 第三重就是在这个区间中找到断点的最佳位置
			 for(int s=1; s<=j-1; s++){	// s是对应[i,i+j]中间的第s个数 
			 	min_now, max_now = get_minmax(i, s, j);
			 	max_num[i][j] = max(max_num[i][j], max_now);
			 	min_num[i][j] = min(min_num[i][j], min_now);
			 }
    int ret = max_num[0][n];
//	for(int i = 0; i<n; i++){
//		ret = max(ret, max_num[i][n]);
//		for (int j=1;j<=n;j++)
//			printf("%d ", max_num[i][j]);
//		printf("\n");
//	}			
	printf("%d", ret);
	return 0;
}
//测试数据:
//4
//+-7+4*2*5

  三重for循环中的前两重的顺序是个细节操作,不可以换位置,因为换位之后会造成计算时用到了没被处理的max_num和min_num,比如计算max_num[0][3]时,当point为1,则max_num=max_num[0][1]+max_num[1][2],此时因为大循环是首个空断边,所以才进行到0,max_num[1][2]还没被计算到,所以仍为0。
在这里插入图片描述
  黄色背景的是被计算过的数据,红色字体的是正在计算的数据,绿色字体的是此时计算所要用到的数据。我们可以发现先循环长度才能保证每次被用到的数据的断点长度point<已有长度len,同时用到的数据max_num[real_point][len-point]也被计算完了。如下图:
在这里插入图片描述

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

铖铖的花嫁

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

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

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

打赏作者

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

抵扣说明:

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

余额充值