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

猜你喜欢

转载自blog.csdn.net/wjh2622075127/article/details/82424846
今日推荐