Nim游戏
一共有N堆石子,编号1…n,第i堆中有个a[i]个石子。
每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子,至少取一颗,至多取出这一堆剩下的所有石子。
两个人轮流行动,取走最后一个的人胜利。Alice为先手。
对于该博弈,我们知道
若
a[1] xor a[2] xor a[3]…xor a[n]==0
先手必败
反之 先手必胜
SG函数(打表)
sg[n]代表对于一堆只有n个的石子,两人轮流拿,若sg[n]==0,则先手必败,反之,必胜
首先易知 sg[0]=0,没有石子,先手必败
随后,对于sg[n]
s
g
[
n
]
=
m
e
x
(
s
g
[
n
−
1
]
,
s
g
[
n
−
2
]
,
.
.
.
)
sg[n]=mex(sg[n-1],sg[n-2],...)
sg[n]=mex(sg[n−1],sg[n−2],...)
mex括号内为所有可以拿取的石子数量
int sg[maxn];//sg[n] n表示每堆数量
int s[k];//每次能取的值,下标从0开始,0 ~ k-1,必须有序,可以sort(s,s+k);
bool vis[maxn];
const int k;//k是集合s的大小
void getsg()
{
int i,j;
for(i=0;i<maxn;i++)
{
memset(vis,0,sizeof(vis));
j=0;
while(j<k&&s[j]<=i)
{
vis[sg[i-s[j]]]=1;
j++;
}
for(j=0;j<maxn;j++)
if(!vis[j])
{
sg[i]=j;
break;
}
}
}
int main()
{
...
memset(sg,-1,sizeof(sg));
getsg();
if(sg[n]==0) //先手必败
else //先手必胜
//如果有多堆,则
// num=sg[n1]^sg[n2]^sg[n3]^....^sg[nx];
// if(num==0) 则先手必败
// else 先手必胜
...
}
对于多堆石子的情况
我们将得到的sg函数
进行xor运算
再次进行判断即可。
阶梯博弈
对于树,1号为根节点。
每个节点都有石子,每次操作可以由一个节点,移动任意数量石子,到该点的父节点,Alice和Bob轮流进行操作,最终无法操作(所有石子都在节点1)的人输掉
对于该树
若Alice将第三层的任何值移动到第二层,Bob随即便可以将其顺势移动到第一层。如此,变对胜负完全没有影响
由此我们发现,移动奇数层的节点没有意义
奇数层的节点等同于根节点
所以我们可以将该模型理解为,所有偶数层节点的一堆石子进行的经典Nim游戏