NEFU OJ Problem1356 帽儿山奇怪的棋盘 题解

题目:

Time Limit:1000ms | Memory Limit:65535K

Description

军哥来到了帽儿山,发现有两位神人在顶上对弈。棋盘长成下图的模样:

每个点都有一个编号:由上到下,由左到右,依次编号为 1、2……12。两位神人轮流博弈,每一轮操作的一方可以取走一个棋子,或者取走相邻的两个棋子(即在同一直线上相邻的棋子)。取走最后一颗棋子的人输。给定初始状态,如果两个人都采取最优决策,问谁能赢。

Input

第一行一个数 n 表示数据组数,接下来 n 行每行一个长度为 12 的 01 串,1 表示该位置上有棋子,0 表示没有。(n<=100)

Output

输出一行,一个长度为 n 的 01 串,0 表示先手赢,1 表示后手赢。

Sample Input

3
110000000000
100000000000
110000000011

Sample Output

010

思路:

利用博弈论思想:
当某状态无论怎么取均变为必胜态,该态为必败态。
当某状态可以转为必败态,该态为必胜态。

·先考虑棋盘状态表示:考虑到棋盘较小, 直接用12位二进制数\(p_0p_1p_2……p_{11}\) 表示棋盘状态。其中\(p_i=1\)表示第i位有棋子。
·再考虑拿法,共分为两种

  1. 拿一个棋子:将位置\(i\)对应位\(p_{i-1}\)置0(\(p_{i-1}\)需为1)
  2. 拿相邻两个棋子:如果直接判断较为复杂,因为棋盘较小,直接用12位数组预存相邻的12种棋子情况。

由以上思路暴力跑一遍所有情况(情况数小于8192)储存在game数组中,输入数据时直接调用对应胜负情况即可。
个人代码如下供大家参考:

#include <bits/stdc++.h>

using namespace std;

const int N = 1<<13;//最大棋盘数值

int game[N];//某棋盘对应胜负情况
int posConnect[12][2] = {
    {1,4},
    {3,4},
    {2,5},
    {5,6},
    {7,8},
    {8,11},
    {9,12},
    {9,10},
    {4,5},
    {4,8},
    {8,9},
    {5,9}
};//所有相连位置


int Dp(int x)//返回1代表胜,0代表败
{
    if(game[x] != -1)
        return game[x];
    //找必败态

    for(int i = 0; i < 12; i ++)//查找取单子情况
    {
        int pos = 1 << i;
        if(pos & x && !Dp(x-pos))//某位置是1,且去掉后为必败态
            return game[x] = 1;
    }


    for(int i = 0; i < 12; i ++)//查找取连子情况
    {
        int pos1 = 1 << posConnect[i][0]-1; //位置转二进制
        int pos2 = 1 << posConnect[i][1]-1; //位置转二进制
        if(pos1 & x && pos2 & x && !Dp(x-pos1-pos2))//相邻两位置都为1,且去掉后为必败态
            return game[x] = 1;
    }

    //没有找到必胜态,必败
    return game[x] = 0;

}

int Func(string s)//处理输入, 字符串转二进制数
{
    int res = 0;
    for (int i = 0; i < 12; i ++)
        if(s[11-i] == '1')
            res += 1 << i;
    return res;
}

int main()
{
    queue<bool> res; // 用队列存每次输入的结果

    memset(game,-1,sizeof game);
    game[0] = 1;
    int n;
    cin >> n;
    while(n--)
    {
        string str;
        cin >> str;
        int x = Func(str);
        res.push(!Dp(x));//存入结果队列
    }

    while(!res.empty())//输出所有结果
    {
        cout << res.front();
        res.pop();
    }

    return 0;
}

总结:

·当情况较少时可以使用暴力dp计算博弈论。

·用二进制数存棋盘局面可以较为方便的实现棋盘存储和运算。其中遇到相连棋子等判断问题,可以先计算预存所有该情况(情况较少时可手动写入)。

posted @ 2023-11-07 08:46  eChorgi  阅读(173)  评论(0)    收藏  举报