Nim游戏
基础Nim游戏
公平组合游戏ICG
若一个游戏满足:
- 由两名玩家交替行动;
- 在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关;
- 不能行动的玩家判负;
则称该游戏为一个公平组合游戏。
NIM博弈属于公平组合游戏,但城建的棋类游戏,比如围棋,就不是公平组合游戏。因为围棋交战双方分别只能落黑子和白子,胜负判定也比较复杂,不满足条件2和条件3。
有向图游戏
给定一个有向无环图,图中有一个唯一的起点,在起点上放有一枚棋子。两名玩家交替地把这枚棋子沿有向边进行移动,每次可以移动一步,无法移动者判负。该游戏被称为有向图游戏。
任何一个公平组合游戏都可以转化为有向图游戏。具体方法是,把每个局面看成图中的一个节点,并且从每个局面向沿着合法行动能够到达的下一个局面连有向边。
NIM游戏
给定 N N N 堆物品,第 i i i 堆物品有 A i A_i Ai 个。两名玩家轮流行动,每次可以任选一堆,取走任意多个物品,可把一堆取光,但不能不取。取走最后一件物品者获胜。两人都采取最优策略,问先手是否必胜。
我们把这种游戏称为 NIM 博弈。把游戏过程中面临的状态称为局面。整局游戏第一个行动的称为先手,第二个行动的称为后手。若在某一局面下无论采取何种行动,都会输掉游戏,则称该局面必败。
所谓采取最优策略是指,若在某一局面下存在某种行动,使得行动后对面面临必败局面,则优先采取该行动。同时,这样的局面被称为必胜。我们讨论的博弈问题一般都只考虑理想情况,即两人均无失误,都采取最优策略行动时游戏的结果。
NIM博弈不存在平局,只有先手必胜和先手必败两种情况。
定理
NIM博弈先手必胜,当且仅当 A 1 ⊕ A 2 ⊕ . . . ⊕ A n ≠ 0 A_1\oplus A_2 \oplus ...\oplus A_n \neq 0 A1⊕A2⊕...⊕An=0 ,反之先手必败。
例子
有两堆石子,第一堆有2个,第二堆有3个,先手必胜。
操作步骤:
- 先手从第二堆拿走1个,此时第一堆和第二堆数目相同
- 无论后手怎么拿,先手都在另外一堆石子中取走相同数量的石子即可
证明
- 当每堆石子的个数为 A 1 ⊕ A 2 ⊕ . . . ⊕ A n = x ≠ 0 A_1\oplus A_2 \oplus ...\oplus A_n =x \neq 0 A1⊕A2⊕...⊕An=x=0 时,
设 x x x 的二进制表示中最高的一位 1 1 1 在第 k k k 位
a 1 ∼ a n a_1\sim a_n a1∼an 中必然有一位 a i a_i ai , a i a_i ai 的第 k k k 位是 1 1 1
则, a i ⊕ x < a i a_i\oplus x<a_i ai⊕x<ai (举例)
那么,从 a i a_i ai 这堆石子中拿走 a i − ( a i ⊕ x ) a_i-(a_i\oplus x) ai−(ai⊕x) ,第 a i a_i ai 堆石子就会变成 a i − ( a i − ( a i ⊕ x ) ) = a i ⊕ x a_i-(a_i-(a_i\oplus x))=a_i\oplus x ai−(ai−(ai⊕x))=ai⊕x
则原石子变为 A 1 ⊕ A 2 ⊕ . . . A i ⊕ x ⊕ . . . ⊕ A n = 0 A_1\oplus A_2 \oplus ...A_i\oplus x\oplus...\oplus A_n =0 A1⊕A2⊕...Ai⊕x⊕...⊕An=0
- 当 a 1 ⊕ a 2 ⊕ … ⊕ a n = 0 a_1⊕a_2⊕…⊕a_n=0 a1⊕a2⊕…⊕an=0,那么无论玩家怎么拿,必然会导致最终异或结果不为 0 0 0 。
反证法:假设玩家从第 i i i 堆石子拿走若干个,结果仍是 0 0 0 。不妨设还剩下 a ′ a′ a′ 个,因为不能不拿,所以 0 ≤ a ′ < a i 0≤a′<a_i 0≤a′<ai ,且 a 1 ⊕ a 2 ⊕ … ⊕ a ′ ⊕ … ⊕ a n = 0 a_1⊕a_2⊕…⊕a′⊕…⊕a_n=0 a1⊕a2⊕…⊕a′⊕…⊕an=0 。
那么, ( a 1 ⊕ a 2 ⊕ … ⊕ a i ⊕ … a n ) ⊕ ( a 1 ⊕ a 2 ⊕ … ⊕ a ′ ⊕ … ⊕ a n ) = a i ⊕ a ′ = 0 (a_1⊕a_2⊕…⊕a_i⊕…a_n)⊕(a_1⊕a_2⊕…⊕a′⊕…⊕a_n)=a_i⊕a′=0 (a1⊕a2⊕…⊕ai⊕…an)⊕(a1⊕a2⊕…⊕a′⊕…⊕an)=ai⊕a′=0,则 a i = a ′ a_i=a′ ai=a′ ,与假设 0 ≤ a ′ < a i 0≤a′<a_i 0≤a′<ai 矛盾。
?为什么使用异或
k − N i m k-Nim k−Nim 游戏
台阶Nim游戏
描述
现在,有一个 n n n 级台阶的楼梯,每级台阶上都有若干个石子,其中第ii级台阶上有 a i a_i ai 个石子 ( i ≥ 1 ) (i≥1) (i≥1) 。
两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。
已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
定理
考虑奇数级台阶上的石子
**台阶Nim游戏先手必胜,当且仅当 A 1 ⊕ A 3 ⊕ . . . ⊕ A n ≠ 0 A_1\oplus A_3 \oplus ...\oplus A_n \neq 0 A1⊕A3⊕...⊕An=0 **
证明
先手时,如果奇数台阶异或非0,根据经典Nim游戏,先手总有一种方式使奇数台阶异或为0,于是先手留了技术台阶异或为0的状态给后手
于是轮到后手:
① 当后手移动偶数台阶上的石子时,先手只需将对手移动的石子继续移到下一个台阶,这样奇数台阶的石子相当于没变,于是留给后手的又是奇数台阶异或为 0 的状态
② 当后手移动奇数台阶上的石子时,留给先手的奇数台阶异或非 0 ,根据经典Nim游戏,先手总能找出一种方案使奇数台阶异或为 0
因此无论后手如何移动,先手总能通过操作把奇数异或为0的情况留给后手,当奇数台阶全为 0 时,只留下偶数台阶上有石子。
(核心就是:先手总是把奇数台阶异或为0的状态留给对面,即总是将必败态交给对面)
Sg函数方法
集合Nim游戏
Sg函数
Mex运算的定义
设 S S S 表示一个非负整数集合。定义 m e x ( S ) mex(S) mex(S) 为求出不属于集合 S S S 的最小非负整数的运算,
即: m e x ( S ) = m i n x mex(S) = min{x} mex(S)=minx , x x x 属于自然数,且 x x x 不属于 S S S
人话:找到集合当中不存在的最小自然数
SG函数的定义
在有向图游戏中,对于每个节点(状态) x x x ,设从 x x x 出发共有 k k k 条有向边,分别到达节点 y 1 , y 2 , … , y k y_1, y_2, …, y_k y1,y2,…,yk ,定义 S G ( x ) SG(x) SG(x) 为 x x x 的后继节点 y 1 , y 2 , … , y k y_1, y_2, …, y_k y1,y2,…,yk 的 S G SG SG 函数值构成的集合再执行 m e x ( S ) mex(S) mex(S) 运算的结果,
即: S G ( x ) = m e x ( { S G ( y 1 ) , S G ( y 2 ) , … , S G ( y k ) } ) SG(x) = mex(\{SG(y_1), SG(y_2), …, SG(y_k)\}) SG(x)=mex({SG(y1),SG(y2),…,SG(yk)})
特别地,整个有向图游戏 G G G 的 S G SG SG 函数值被定义为有向图游戏起点 s s s 的 S G SG SG 函数值,即 S G ( G ) = S G ( s ) SG(G) = SG(s) SG(G)=SG(s), S G ( 终 点 ) = 0 SG(终点)=0 SG(终点)=0。
性质
- SG(i)=k,则i最大能到达的点的SG值为k−1。1.SG(i)=k,则i最大能到达的点的SG值为k−1。
- 非0可以走向0
- 0只能走向非0
总结
当 S G ( x ) = 0 SG(x)=0 SG(x)=0 时,说明这个状态走不到终点。
当 S G ( x ) ≠ 0 SG(x)\neq 0 SG(x)=0 时,说明这个状态的点可以到达终点。
定理
对于 n n n 个图,如果 S G ( G 1 ) ⊕ S G ( G 2 ) ⊕ … S G ( G n ) ≠ 0 SG(G1) \oplus SG(G2) \oplus … SG(Gn)\ne 0 SG(G1)⊕SG(G2)⊕…SG(Gn)=0 ,则先手必胜,反之必败
证明(类似于Nim游戏)
① 当 S G ( G i ) = 0 SG(Gi)=0 SG(Gi)=0 时 , x o r = 0 xor=0 xor=0 , 显然先手必败
(PS:结束状态必是状态①,但状态①不一定是结束状态)
② 当 x o r = x ≠ 0 xor=x\ne 0 xor=x=0,因为肯定存在一个 S G ( x i ) ⊕ x < S G ( x i ) SG(xi)\oplus x <SG(xi) SG(xi)⊕x<SG(xi) ,而根据 S G ( ) SG() SG() 的性质1可知, S G ( k ) SG(k) SG(k) 可以走到 0 − k − 1 0−k−1 0−k−1 的任何一个状态,因此,必定可以从 S G ( x i ) → S G ( x i ) ⊕ x SG(xi)\to SG(xi)\oplus x SG(xi)→SG(xi)⊕x , 于是使得 x o r = 0 xor=0 xor=0
③当 x o r = 0 xor=0 xor=0 时,当移动任何一个节点时,对应的SGSG值必然减小,可以证明: x o r ≠ 0 xor\ne 0 xor=0
下证: x o r ≠ 0 xor\ne 0 xor=0
假设: x o r = 0 xor=0 xor=0 ,则说明移动的那个节点的值并没有变化,即从 S G ( k ) SG(k) SG(k) 变成了 k k k,但是这与 S G SG SG 函数的性质1相矛盾,因此不成立
证得:若先手面对的状态是 x o r ≠ 0 xor\ne 0 xor=0 ,则先手方总能使 x o r = 0 xor=0 xor=0 ,即使后手面对的永远是必败态直到结束状态①,因此先手必胜。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7, M = N * 2;
int s[N], f[N]; //s存可行的集合 f存所有状态
int n, k;//k是集合中数字的个数,n是石子的堆数
int sg(int x) {
if(f[x] != -1) return f[x]; //记忆化搜索,如果f[x]已经被计算过,则直接返回
unordered_set<int> S; //用一个哈希表来存每一个局面能到的所有情况,便于求mex
for(int i = 1; i <= k; i++) {
int sum = s[i];
if(x >= sum) S.insert(sg(x - sum)); //如果可以减去s[i],则添加到S中
}
for(int i = 0;; i++) { //求mex(),找到最小的且不在集合中的数
if(!S.count(i)) return f[x] = i;
// count() 返回匹配给定主键的元素的个数,即count(value)中value出现的次数
}
}
int main() {
cin >> k;
for(int i = 1; i <= k; i++) cin >> s[i];
memset(f, -1, sizeof f);
cin >> n;
int res = 0;
while(n--) {
int t;
cin >> t; //读入每堆石子
res ^= sg(t);
}
if(res) puts("Yes");
else puts("No");
return 0;
}