gym102800J记忆化搜索(对抗搜索)

本文介绍了一个3x3棋盘的博弈问题,玩家Alice和Bob交替落子,目标是使自己的棋子连成一线以获取分数。通过哈希地图状态并采用记忆化搜索策略,实现了对所有可能游戏状态的有效遍历。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

https://codeforces.com/gym/102800/attachments

一个3*3的棋盘残局,有三种字符:

  • 字符.表示此处还未下棋。
  • 字符O表示这是Alice的棋。
  • 字符X表示这是Bob的棋。

注意,所给的残局不一定是合法的残局,也即可能一方的棋子更多。

当一方的棋连成三个时(可以斜着、横着、竖着),此人得一分。最后棋盘下满后,这局游戏的得分为:Alice的分数-Bob的分数。

现在,输入1表示Alice先手、0表示Bob先手,双方轮流下棋。Alice想要得分最大,Bob想要得分最小,双方都最优的策略下,问这局的最后得分是多少?


Sample Input 1   |   Sample Input 2
0                |   1
.OO              |   XXX
X.O              |   XXX
OXO              |   XXX
                 | 
Sample Output 1  |   Sample Output 2
2                |   -8

解题思路

将地图哈希成一个数值,最多有 3 9 3^9 39张地图,不到20000,暴力枚举所有情况即可,使用记忆化搜索剪枝。

AC代码

#include <bits/stdc++.h>
using namespace std;

unordered_map<string, int> num;

string pre[20010];

int id; //20000

int dp[20010][2]; // 地图i  轮到j
void get_id(string a)
{
    if (a.size() == 9)
    {
        num[a] = ++id;
        pre[id] = a;
        return;
    }
    get_id(a + '.');
    get_id(a + 'O');
    get_id(a + 'X');
}

int get_score(string &a)
{
    int res = 0;
    if (a[0] == a[1] && a[1] == a[2])
        res += a[1] == 'O' ? 1 : -1;
    if (a[3] == a[4] && a[4] == a[5])
        res += a[4] == 'O' ? 1 : -1;
    if (a[6] == a[7] && a[7] == a[8])
        res += a[7] == 'O' ? 1 : -1;
    if (a[0] == a[3] && a[3] == a[6])
        res += a[3] == 'O' ? 1 : -1;
    if (a[1] == a[4] && a[4] == a[7])
        res += a[4] == 'O' ? 1 : -1;
    if (a[2] == a[5] && a[5] == a[8])
        res += a[5] == 'O' ? 1 : -1;
    if (a[0] == a[4] && a[4] == a[8])
        res += a[4] == 'O' ? 1 : -1;
    if (a[2] == a[4] && a[4] == a[6])
        res += a[4] == 'O' ? 1 : -1;
    return res;
}
int dfs(int st, int opt)
{
    string s = pre[st];
    if (dp[st][opt] != -1)
        return dp[st][opt];
    int mm = 10, mx = -10, flag = 0;
    for (int i = 1; i <= 3; i++)
        for (int j = 1; j <= 3; j++)
        {
            if (s[(i - 1) * 3 + j - 1] == '.')
            {
                flag = 1;
                s[(i - 1) * 3 + j - 1] = (opt ? 'O' : 'X');
                int temp = dfs(num[s], opt ^ 1);
                s[(i - 1) * 3 + j - 1] = '.';
                mm = min(mm, temp);
                mx = max(mx, temp);
            }
        }
    if (!flag)
        return dp[st][opt] = get_score(s);
    if (opt == 1)
        return dp[st][opt] = mx;
    else
        return dp[st][opt] = mm;
}

char ss[50];
int main()
{
    memset(dp, -1, sizeof dp);
    get_id("");
    dfs(1, 1);
    dfs(1, 0);
    int t;
    scanf("%d", &t);
    while (t--)
    {
        int opt;
        scanf("%d", &opt);
        for (int i = 0; i < 3; i++)
            scanf("%s", ss + i * 3);
        string aa = "";
        for (int i = 0; i < 9; i++)
            aa += ss[i];
        cout << dfs(num[aa], opt) << endl;
    }

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hesorchen

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

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

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

打赏作者

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

抵扣说明:

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

余额充值