201803-4 棋局评估(动态规划+优先队列)

试题编号: 201803-4
试题名称: 棋局评估
时间限制: 1.0s
内存限制: 256.0MB
问题描述:

问题描述

  Alice和Bob正在玩井字棋游戏。
  井字棋游戏的规则很简单:两人轮流往3*3的棋盘中放棋子,Alice放的是“X”,Bob放的是“O”,Alice执先。当同一种棋子占据一行、一列或一条对角线的三个格子时,游戏结束,该种棋子的持有者获胜。当棋盘被填满的时候,游戏结束,双方平手。
  Alice设计了一种对棋局评分的方法:
  - 对于Alice已经获胜的局面,评估得分为(棋盘上的空格子数+1);
  - 对于Bob已经获胜的局面,评估得分为 -(棋盘上的空格子数+1);
  - 对于平局的局面,评估得分为0;


  例如上图中的局面,Alice已经获胜,同时棋盘上有2个空格,所以局面得分为2+1=3。
  由于Alice并不喜欢计算,所以他请教擅长编程的你,如果两人都以最优策略行棋,那么当前局面的最终得分会是多少?

输入格式

  输入的第一行包含一个正整数T,表示数据的组数。
  每组数据输入有3行,每行有3个整数,用空格分隔,分别表示棋盘每个格子的状态。0表示格子为空,1表示格子中为“X”,2表示格子中为“O”。保证不会出现其他状态。
  保证输入的局面合法。(即保证输入的局面可以通过行棋到达,且保证没有双方同时获胜的情况)
  保证输入的局面轮到Alice行棋。

输出格式

  对于每组数据,输出一行一个整数,表示当前局面的得分。

样例输入

3
1 2 1
2 1 2
0 0 0
2 1 1
0 2 1
0 0 2
0 0 0
0 0 0
0 0 0

样例输出

3
-4
0

样例说明

  第一组数据:
  Alice将棋子放在左下角(或右下角)后,可以到达问题描述中的局面,得分为3。
  3为Alice行棋后能到达的局面中得分的最大值。
  第二组数据:


  Bob已经获胜(如图),此局面得分为-(3+1)=-4。
  第三组数据:
  井字棋中若双方都采用最优策略,游戏平局,最终得分为0。

数据规模和约定

  对于所有评测用例,1 ≤ T ≤ 5。

 通过对每一种棋局定义一个唯一的标识来实现动态规划,对于不同的下棋方,利用重装载<的优先队列来实现不同的最优要求(Alice想要评分最大,Bob想要评分最小)。


#include <map>  
#include <cmath>  
#include <queue>  
#include <cstdio>  
#include <string>  
#include <cstring>  
#include <iostream>  
#include <algorithm> 
#include <sstream> 
#include <time.h> 
#include <vector>
#include <list>
 
using namespace std;
//201803-4  棋局评估
struct Chess {
	int num[3][3];	//当前局势
	int Sum;		//局势的唯一标识
	bool flag;		//表示该由谁下子
	int score;		//得分
};
map<int, Chess>Array;	//存放算出分数的局势

struct My_int {
	int score;
	bool Flag;
	bool operator <(const My_int& s)const
	{
		if (Flag == 1)
			return score > s.score;		//最小堆
		else
			return score < s.score;			//最大堆
	}
};
void Get_Num(Chess &D)
{
	int Sum = 0;
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			cin >> D.num[i][j];
			Sum += D.num[i][j] * pow(3, i * 3 + j);
		}
	}
	D.flag = 0;		//该由Alice放X
	D.Sum = Sum;
}
bool Judge(int num[3][3], int &p)
{
	int Zreo_Num = 0;
	int ff = 0;
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			if (num[i][j] == 0)
				Zreo_Num++;
		}
	}
	for (int i = 0; i < 3; i++)
	{
		if (num[i][i] != 0)
		{
			if ((num[i][0] == num[i][1] && num[i][1] == num[i][2]) || (num[0][i] == num[1][i] && num[1][i] == num[2][i]))
			{
				ff = num[i][i]; break;
			}
			else if (i == 1 && ((num[0][0] == num[1][1] && num[1][1] == num[2][2]) || (num[0][2] == num[1][1] && num[1][1] == num[2][0])))
			{
				ff = num[i][i]; break;
			}
		}
	}
	if (ff == 0 && Zreo_Num == 0)	//棋局已满且没有胜出方
	{
		p = 0; return 1;		//平局,结束
	}
	else if (ff == 1)	//Alice胜出
	{
		p = Zreo_Num + 1; return 1;	//结束
	}
	else if (ff == 2)	//Bob胜出
	{
		p = -Zreo_Num - 1; return 1;	//结束
	}
	else
		return 0;	//游戏没结束
}
int Find_Score(Chess D)
{
	if (Array.find(D.Sum) == Array.end())	//表示还没有计算过这种局势的分数
	{
		int p;
		if (bool F = Judge(D.num, p))		//已经分出胜负
			return p;
		priority_queue<My_int> S;
		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				if (D.num[i][j] == 0)
				{
					Chess Save = D;
					Save.num[i][j] = 1 + D.flag;
					Save.Sum += Save.num[i][j]*pow(3, i * 3 + j);
					Save.flag = !D.flag;	//更改下子的人
					My_int s;
					s.score = Find_Score(Save);
					s.Flag = D.flag;
					S.push(s);
				}
			}
		}
		D.score = S.top().score;
		Array[D.Sum] = D;		//保存当前局势结果进入数组
		return D.score;		//返回最优
	}
	else
		return Array[D.Sum].score;
}
int main()
{
	int i, j, N, M;
	Chess Data;
	cin >> N;

	//int mm = Find_Score(Data);

	for (i = 0; i < N; i++)
	{
		Get_Num(Data);
		M = Find_Score(Data);
		cout << M << endl;
	}

	cin >> N;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yanpr919/article/details/81265094