公平组合游戏(Impartial Combinatorial Games)
条件:
1.两名选手交替进行操作
2.两名选手局面移动的权力相同
3.当前选手的合法移动集合至于当前的局面有关,与轮到哪名选手操作、以前的任何操作、骰子的点数或者其它什么因素无关
4.如果当前选手的合法移动集合为空,则判此选手负
用状态图来理解就是:将一个游戏中的所有可能出现的状态看作是图的节点,如果一个状态可以一步转移到另一个状态,则这两个状态之间有一条有向边,设初始棋子在任意一节点上,通过有向边两名选手交替移动棋子,直至棋子不能再被移动结束。
例如:n=6,m=3的巴什博弈状态图如下:
以下讨论的是游戏的状态图为有向无环图的情况,即不存在平局
必胜点和必败点的概念:
P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败。
N点:必胜点,同上
性质:
1.P点的所有后继点都是N点
2.N点得后继点中存在P点
3.终结点一定是P点
SG函数:(针对解决公平组合游戏,不存在平局,即无环)
对于任意状态x,
SG(x)=mex{ SG(y) | y为x的所有后继状态 }
如果知道一个状态的SG值,我们就可以快速判断出它是P点还是N点
如果SG值等于0,那么是P点,否则是N
证明上述结论的链接:https://blog.youkuaiyun.com/philipsweng/article/details/48395375
游戏的和:
我们可以定义有向无环图游戏的和(Sum of Graph Games):设G1、G2、……、Gn是n个有向图游戏,定义游戏G是G1、G2、……、Gn的和(Sum),游戏G的移动规则是:任选一个子游戏Gi 并移动上面的棋子。
SG定理:
SG(G)=SG(G1)^SG(G2)^…^SG(Gn)
即游戏的和的SG函数值是它的所有子游戏的SG函数值的异或。
证明上述结论的链接:https://blog.youkuaiyun.com/philipsweng/article/details/48395375
应用:
运用游戏的和,SG函数,SG定理我们可以将一个游戏分解为若干子游戏,分别求解若干子游戏的SG函数值,再将它们异或起来,得到总游戏的SG值。例如下面这个例子:
n堆石子,每次可以从第1堆石子里取1颗、2颗或3颗,可以从第2堆石子里取奇数颗,可以从第3堆及以后石子里取任意颗…… 我们可以把它看作3个子游戏,第1个子游戏只有一堆石子,每次可以取1、2、3颗,很容易看出x颗石子的局面的SG值是x%4。第2个子游戏也是只有一堆 石子,每次可以取奇数颗,经过简单的画图可以知道这个游戏有x颗石子时的SG值是x%2。第3个游戏有n-2堆石子,就是一个nim游戏。对于原游戏的每 个局面,把三个子游戏的SG值异或一下就得到了整个游戏的SG值。
经典题:
poj1848
#include<bits/stdc++.h>
using namespace std;
#define maxn 1010
#define N 20
int S[maxn];
int SG[maxn];
int f[N];
void getSG()
{
memset(SG,0,sizeof SG);
for(int i=1;i<=maxn;i++)
{
memset(S,0,sizeof S);
for(int j=0;f[j]<=i&&j<=N;j++)
S[SG[i-f[j]]]=1;
for(int j=0;;j++)
{
if(!S[j])
{
SG[i]=j;
break;
}
}
}
return;
}
int main()
{
int m,n,p;
f[0]=1;
f[1]=1;
for(int i=2;i<=20;i++)
{
f[i]=f[i-1]+f[i-2];
}
getSG();
while(scanf("%d%d%d",&m,&n,&p),m||n||p)
{
if(SG[m]^SG[n]^SG[p])
printf("Fibo\n");
else
printf("Nacci\n");
}
return 0;
}