HDU - 5724 Chess——SG函数

题意:

给你一个n*20的棋盘,每一行都有一些棋子,每次你可以把一个棋子移动到他右面的空位中离他最近的那一个,不能移动者输,Alice先手,问Alice能否取胜

思路:

题目叙述满足SG函数的使用条件,因此考虑使用SG函数解决问题

大体思路是求出每一行的状态对应的SG值,最后把所有行的SG值进行异或运算,最终结果如果不等于0,则输出YES,否则输出NO

如何设计SG函数呢?每一行的状态可以用一个20位的二进制数表示,求每行的状态对应的SG值实际上就是求每行的二进制数对应的SG值,而我们可以预处理出1~1<<20所有状态的SG的,至此问题解决

关于预处理:

稍加分析就会发现,一个二进制数对应的状态的子状态的二进制数一定比他小,根据这一点我们可以从0到1<<20递推,对于每个二进制数,首先把它提取到数组中(0对应最低位),然后根据这个数组构造出子状态(这里的骚操作参考代码),最后根据SG函数的定义求解当前二进制数的SG值

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = (1<<20) + 10;
int T, n, m, p;
bool vis[25];
int sg[maxn], num[25];
void init() {
    memset(sg, 0, sizeof(sg));
    for (int i = 0; i < (1<<20); i++) {
        memset(vis, false, sizeof(vis));
        for (int j = 0; j < 20; j++) {
            if (i & (1<<j)) num[j] = 1;
            else num[j] = 0;
        }
        int zero = -1;
        for (int j = 0; j < 20; j++) {
            if (num[j] == 0) zero = j;
            else {
                if (zero == -1) continue;
                else {
                    vis[sg[i ^ (1<<j) ^ (1<<zero)]] = true;
                }
            }
        }
        for (int j = 0; j < maxn; j++) if (!vis[j]) { sg[i] = j; break; }
    }
}
int main() {
    init();
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &m);
            int sum = 0;
            for (int j = 1; j <= m; j++) {
                scanf("%d", &p);
                sum += (1<<(20-p));
            }
            ans ^= sg[sum];
        }
        if (ans) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

阅读更多
版权声明:欢迎大家转载,转载请注明出处 https://blog.csdn.net/hao_zong_yin/article/details/80316353
个人分类: 博弈
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

HDU - 5724 Chess——SG函数

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭