893. 集合-Nim游戏

时间:2021-10-11
本文章向大家介绍893. 集合-Nim游戏,主要包括893. 集合-Nim游戏使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目传送门

SG函数

SG函数是解决博弈论问题的一把利器。

补充

1、\(Mex\)运算

\(S\)表示一个自然数集合.定义\(Mex(S)\)为求出不属于集合\(S\)的最小自然数运算。 自然数是指非负整数,包括零。

例如:\(S=\{0,1,2,4\}\),那么\(Mex(S)=3\)

2、\(SG\)函数

在有向图游戏中,对于每个节点\(x\),设从\(x\)出发共有\(k\)条有向边,分别到达节点\(y_1\),\(y_2\),····\(y_k\),

\(SG(x)=Mex(\{SG(y_1),SG(y_2)····SG(y_k)\})\)

整个有向图游戏\(G\)\(SG\)函数值被定义为有向图游戏起点\(s\)\(SG\)函数值,即 \(SG(G)=SG(s)\)

终点的\(SG\)函数定义成\(0\)

3、有向图游戏的和

设G1,G2,····,Gm是m个有向图游戏.定义有向图游戏G,他的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步.G被称为有向图游戏G1,G2,·····,Gm的和.
有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数的异或和,即:
SG(G)=SG(G1) ^ SG(G2) ^ ··· ^ SG(Gm)

4、延伸阅读

https://zhuanlan.zhihu.com/p/20611132

其中与本题有关的是第五节。简要来说,「异或」运算的发现,有这么两条线索:用一些 SG 数较小的状态作为例子,观察发现状态的组合对应着 SG 数的异或;提炼出状态组合的四条性质:交换律、结合律、归零律、恒等律,而异或运算也具有这四条性质。然而这些都只是「线索」,最终还是要用数学归纳法去证明状态的组合对应着 SG 数的异或。

C++ 代码

#include <bits/stdc++.h>

using namespace std;

// 理论参考
// https://www.cnblogs.com/greenpepper/p/11235232.html

const int N = 110, M = 10010;

int n, m;
int s[N]; //一共几种取法,比如一次取2个或5个。
int f[M]; //SG函数的值

//记忆化搜索来做,保证每个状态只被算一次
int sg(int x) {
    //记忆化搜索
    if (f[x] != -1) return f[x];

    //它所有可以到的局面
    unordered_set<int> S;
    for (int i = 0; i < m; i++) {
        int sum = s[i];
        if (x >= sum) S.insert(sg(x - sum));//如果够取的话,那么就去取一下
    }
    //找出集合中不存在的这个自然数
    for (int i = 0;; i++)
        if (!S.count(i))
            return f[x] = i;
}

int main() {
    cin >> m; //一共几个允许的个数
    for (int i = 0; i < m; i++) cin >> s[i]; //允许多少个拿取的数量,本题中是2和5

    cin >> n;//一共几堆

    //初始化数组值为-1
    memset(f, -1, sizeof f);
    int res = 0;

    //n堆石子的SG函数,异或在一起,就像是基础NIM游戏一样,取得是否必胜还是必败
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x; //每堆里多少个
        res ^= sg(x);
    }

    if (res) puts("Yes");   //必胜
    else puts("No");        //必败

    return 0;
}

原文地址:https://www.cnblogs.com/littlehb/p/15392613.html