试题编号: | 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(MAX)下X,Bob(MIN)下O,直到到达了树的终止状态即一位棋手占领一行,一列、一对角线或所有方格都被填满。Utility指效用函数,定义游戏者在状态S下的数值。在这道题中,就是指:
- 对于Alice已经获胜的局面,评估得分为(棋盘上的空格子数+1);
- 对于Bob已经获胜的局面,评估得分为 -(棋盘上的空格子数+1);
- 对于平局的局面,评估得分为0;
所以,在上图策略树中,无论当前局势如何,Alice(MAX)总会选择最大的评估分对应的走法,Bob(MIN)总会选择最小的评估分对应的走法。这样才能使自己尽快的赢得比赛(这一点是关键,要想清楚)。题目中只给出了策略树中叶子节点的评估分的计算方法(赢,输或平局情况的评估分计算方法),那如何计算策略树中每个非叶子节点对应的评估分值呢?
答案是采用深度优先搜索对整个策略树进行后序遍历,这样,先计算策略树中叶子节点的评估值,在一层层的往上计算非叶子节点的评估值,最终,会得到整个策略树的评估值,这样就可以确定玩家在当前情况下应该如何走棋了。
根据以上思路:
#include <iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
int map[10], T;
int check()
{
int it = 0;
for (int i = 1; i <= 3; ++i) { // 找出是否可以结束。
if (map[i] == map[i + 3] && map[i + 3] == map[i + 6] && map[i]) {
it = map[i];
break;
}
int k = 3*(i - 1) + 1;
if (map[k] == map[k + 1] && map[k] == map[k + 2] && map[k]) {
it = map[k];
break;
}
}
if (!it) {
if (map[1] == map[5] && map[1] == map[9] && map[1]) it = map[1];
else if (map[3] == map[5] && map[5] == map[7] && map[5]) it = map[5];
}
int cnt = 0;
for (int i = 1; i <= 9; ++i)
if (map[i] == 0) cnt++;
if (it == 0 && cnt == 0) return 0;
if (it == 1) return cnt + 1;
else if (it == 2) return - (cnt + 1);
else return -1;
}
int dfs(int it)
{
int chec = check();
if (chec != -1) return chec;
int ans = it == 1 ? -INF : INF;
for (int i = 1; i <= 9; ++i) {
if (map[i]) continue;
if (it == 1) {
map[i] = 1;
ans = max(ans, dfs(2));
} else {
map[i] = 2;
ans = min(ans, dfs(1));
}
map[i] = 0;
}
return ans;
}
int main()
{
cin >> T;
while (T--) {
for (int i = 1; i <= 9; ++i) {
cin >> map[i];
}
cout << dfs(1) << endl;;
}
}