[Sicily 1176 Two Ends] 动态规划 记忆化搜索

博客介绍了Sicily 1176游戏问题,玩家1对抗采用贪心策略的玩家2。游戏规则是两人轮流从卡片行的两端取卡片,最终得分高者获胜。玩家1的目标是在玩家2贪心取卡的情况下最大化自己的得分。通过动态规划和记忆化搜索,分析如何制定策略以最大限度地赢取分数。
摘要由CSDN通过智能技术生成

(1)问题描述

From Sicily: 1176.Two Ends

题目的意思是: 

给出一行个数为偶数的从左到右排列的卡片,每个卡片上写有一个正整数。从玩家1开始,两个玩家轮流从那一行卡片的最左端或是最右端取一张卡片。最后计算两个玩家所得到的总分,总分大者为赢。玩家2使用的是贪心策略,也就是他每次都会取两端中最大的那一个卡片。然而贪心策略并不总是最好的。现在的问题是:假设玩家1足够聪明,在玩家2使用贪心策略的情况下,玩家1最多可以赢玩家2多少分。


(2)举个例子

假设一个输入样例是这样的:(第一个数代表卡片的个数为4,后面4个数为每个卡片的数值)

4 3 2 10 4

那么对应的输出应该是这样的:

In game 1, the greedy strategy might lose by as many as 7 points.

为什么是7 points?
玩家1选3                                卡片为2 10 4
玩家2选2和4中最大的,选4    卡片为2 10
玩家1选10                              卡片为2
玩家2选2                                游戏结束
玩家1得分3 + 10 = 13
玩家2得分4 + 2 = 6
玩家1 - 玩家2 = 7

(3)动态规划(Dynamic Programming)
动态规划的本质,是对问状态的定义状态转移方程的定义
a.要使用动态规划解决这个问题,首先要思考状态是什么:
定义数组dp[l][r],表示最左边的卡片下标为为l,最右边的卡片下标为r的时候,玩家1可以得到的最大的分数。
b.然后思考状态转移方程的构建:
dp[l][r]如何从前面的状态转移过来?
有两种情况:
情况一:玩家1选的是左端的卡片
             如果玩家1选完之后,左端的卡片比右端的卡片大,那么玩家2选的是左端的卡片,玩家1得分 = card[l] + dp[l - 2][r]
        如果玩家1选完之后,左端的卡片比右端的卡片小,那么玩家2选的是右端的卡片,玩家1得分 = card[l] + dp[l + 1][r - 1]
情况二:玩家1选的是右端的卡片
              如果玩家1选完之后,左端的卡片比右端的卡片大,那么玩家2选的是左端的卡片,玩家1得分 = card[r] + dp[l + 1][r - 1]
              如果玩家1选完之后,左端的卡片比右端的卡片小,那么玩家2选的是右端的卡片,玩家1得分 = card[r] + dp[l][r - 2]
比较这两种情况的得分,dp[l][r] = max(情况一得分,情况二得分);

(4)记忆化搜索
这样搜索会有重复的时候,所以对于已经计算过的状态不再计算,记忆化下来。
具体表现为初始化dp[i][j]为-1,如果后来搜索发现已经被赋值,就直接返回。

(5)代码实现
#include<iostream>
using namespace std;
#define MAX 1001
int card[MAX];
int dp[MAX][MAX];
void init()
{
	fill(card, card + MAX, -1);
	for(int i = 0; i < MAX; i++)
		fill(dp[i], dp[i] + MAX, -1);
}
int max_card(int l, int r)
{
	return (card[l] > card[r] ? card[l]: card[r]);
}
int solve(int l, int r)
{
	int t1 = 0;
	int t2 = 0;
	if(r - l == 1)
		return max_card(l, r);
	if(dp[l][r] != -1)
		return dp[l][r];
	else 
	{
		if(card[l + 1] >= card[r])
			t1 = card[l] + solve(l + 2, r);
		else
			t1 = card[l] + solve(l + 1, r - 1);

		if(card[l] >= card[r - 1])
			t2 = card[r] + solve(l + 1, r - 1);
		else
			t2 = card[r] + solve(l, r - 2);
	}
	dp[l][r] = (t1 > t2 ? t1: t2);
	return dp[l][r];
}
int main()
{
	int n;
	int game_index = 1;
	while(cin >> n && n != 0)
	{
		init();
		int sum = 0;
		for(int i = 0; i < n; i++)
		{
			cin >> card[i];
			sum += card[i];
		}
		int player1 = solve(0, n - 1);
		int player2 = sum - player1;
		cout << "In game " << game_index << ", the greedy strategy might lose by as many as " << player1 - player2 << " points." << endl;
		game_index++;
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值