SG
SG函数
NIM博弈
概述:有\(n\)堆物品,每堆物品有\(A[i]\)个,有两名玩家轮流操作,每一次都可以在其中一堆拿任意多个,但不能不拿,取走最后一件物品者获胜,在双方都使用最优策略的情况下,问先手是否必胜
定理:
\(nim\)游戏先手必胜,当且仅当
即\(A\)的异或和不为0时先手必胜
扩展:
1.\(nim-k\)游戏:有\(n\)堆石子,每次可以从\(k\)堆中拿出任意数量的石子,谁不能拿谁输
先手必胜条件:将每一堆石子个数进行二进制分解,统计二进制位上每一位的1的个数(将每个数二进制第i位相加),再对每一位分别\(\bmod (k+1)\),若有一位上的数\(\bmod (k+1)\)不为0,则先手必胜,全为0则必败
2.反\(-nim\)博弈,即将\(nim\)博弈改为取走最后一件物品的输:
先手必胜条件:当每堆石子都只有1个,且异或和为0,或者至少有一堆石子超过1个,且异或和不为0时,先手必胜
3.阶梯博弈:有n层阶梯(地面为第0层),每一层上有若干个石子,每一次操作可以将一个阶梯上的石子往下推,推到0层就消失,二人轮流操作,没石子推者输
该问题等价于将所有奇数阶梯上的石子做\(nim\)博弈,因为偶数阶不会影响奇数阶相对关系
公平组合游戏ICG(Impartial Combinatorial Games)
若一个游戏满足:
1.双方轮流操作
2.在游戏进程中的任意时刻,双方可选择的操作与轮到哪位玩家无关
3.不能行动者判负
则称这个游戏为公平组合游戏
nim游戏便是一个典型的ICG,但常见的棋类游戏如围棋便不是,因为不满足2,3
有向图游戏DG(Digraph game)
给定一个有向无环图,只有唯一的一个起点,其上有一枚棋子,两名玩家可以轮流将棋子向其中一条有向边进行移动一步,无法行动者判负,则称该游戏为有向图游戏
任意一个ICG都可以转换为有向图游戏,具体方法是:将每一个局面看作有向图中的一个点,将一个局面可以通过合法操作到达的下一个局面连有向边
mex运算
设\(\mathbb{S}\)表示一个非负整数集合,定义\(mex{(\mathbb{S})}\)为不存在于\(\mathbb{S}\)的最小整数,即:
SG函数
在有向图游戏中,设从节点\(x\)出发有\(k\)条有向边,分别连上\(y_1\sim y_k\),则定义
通俗的讲,\(x\)的\(SG\)函数值是对它的后继结点的\(SG\)函数值组成的集合执行\(mex\)运算的结果
特别的,我们定义一个有向图游戏的\(SG\)函数值为有向图起点的\(SG\)函数值
有向图游戏的和
给定\(m\)个有向图游戏\(G_1,G_2……G_m\),定义有向图游戏\(G\),它的游戏规则是任选一个有向图游戏\(G_i\)并在\(G_i\)上移动一步,此时\(G\)被称为有向图游戏\(G_1,G_2…G_m\)的和
定义它的\(SG\)函数值:
简单的说就是有向图游戏的\(SG\)函数值等于它所包含的子有向图游戏的\(SG\)函数值的异或和
定理
1.有向图游戏某个局面必败,当且仅当这个局面的\(SG\)函数值为0
2.有向图游戏某个局面必胜,当且仅当这个局面的\(SG\)函数值大于0
例题:
魔法珠(https://www.acwing.com/problem/content/description/237/)
\(Freda\) 和 \(rainbow\) 是超自然之界学校(Preternatural Kingdom University,简称\(PKU\))魔法学院的学生。
为了展示新学的魔法,他们决定进行一场对弈。
起初 \(Freda\) 面前有 \(n\)堆魔法珠,其中第 \(i\) 堆有 \(a_i\) 颗。
\(Freda\) 和 \(rainbow\) 可以轮流进行以下操作:
·选择 \(n\) 堆中魔法珠数量大于 \(1\) 的任意一堆。记该堆魔法珠的数量为 \(p\),\(p\) 有 \(b_1、b_2……b_m\) 这 \(m\) 个小于 \(p\) 的约数。
·施展魔法把这一堆魔法珠变成 \(m\) 堆,每堆各有 \(b_1、b_2……b_m\) 颗魔法珠。
·选择这 \(m\) 堆中的一堆魔法珠,施展魔法令其消失。
注意一次操作过后,魔法珠的堆数会增加 \(m−2\),各堆中魔法珠数量的总和可能会发生变化。
当轮到某人操作时,如果每堆中魔法珠的数量均为 \(1\),那么他就输了。
\(Freda\) 和 \(rainbow\) 都采取最好的策略,从 \(Freda\) 开始。
请你预测一下,谁能获胜呢?
分析,容易证明这是一个公平组合游戏,于是我们可以考虑使用\(SG\)函数来解决这个问题
下面我们从两个层面来分析这个问题
1.这是个怎样的有向图游戏
2.这个有向图游戏的有向边(也即策略)是什么
经过缜密思考不难发现,整个有向图游戏就是一堆魔法珠,而所对应的子有向图游戏就是每一堆分成的若干堆魔法珠,子有向图游戏所连接的有向边便是删去任意一堆的操作。于是我们设\(G_1\sim G_n\)为各个子有向图游戏,那么它们的\(SG\)函数值就是对一个集合执行\(mex\)运算的结果,而这个集合便是那个子有向图游戏的所分成的\(m\)个约数中选出\(m-1\)个所进行异或的结果(因为这是的对各个数即子有向图游戏的和),很明显应该有\(C_m^{m-1}=m\)个元素,最后求出了每个数的\(SG\)函数值,将其异或起来便得到了最终答案。当然,这是一个递归求解的过程,语言表述可能不太明白,代码能解释一切
使用记忆化搜索实现(一般SG函数都用记忆化搜索)
#define int long long
int sg[1005],a[105];
int SG(int x){
if(sg[x]!=-1)return sg[x];
if(x==1)return 0;
sg[x]=0;//必须放在这里!
int t=sqrt(x),tot=0;
int vis[1005],ys[1005];
memset(vis,0,sizeof vis);
for(int j=1;j<=t;j++){
if(x%j==0){
ys[++tot]=j;
if(j*j!=x)ys[++tot]=x/j;
}
}
int ans=0;
for(int i=1;i<=tot;i++)ans^=SG(ys[i]);
for(int i=1;i<=tot;i++)vis[ans^SG(ys[i])]=1;
while(vis[sg[x]])sg[x]++;
return sg[x];
}
signed main(){
int n;memset(sg,-1,sizeof sg);
while(~scanf("%lld",&n)){
int ans=0;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++)ans^=SG(a[i]);
if(ans)puts("freda");
else puts("rainbow");
}
return 0;
}
总结:在使用SG函数求解博弈论问题的过程中,我们应该认真分析,分清楚哪个是最终的有向图游戏,哪些是子有向图游戏,哪些是有向图游戏连接的边,方法是最终题目给的问题一般是最终有向图游戏,它的形式可能于子有向图游戏不一样,子有向图游戏就是操作过程中发生的类似递归似相同结构的问题操作,而有向图游戏连接的边一般为父有向图游戏到子有向图游戏进行的操作