1.SG函数和SG定理是一个十分神奇的东西,有了它,绝大部分的博弈都可以被统一到这个上面,都可以使用SG函数解决。是一种解决博弈问题的十分方便的手段。
2.首先给出一些基本的定义:mex运算,这个是作用在集合上的运算,具体的含义就是:找出不属于当前集合最小的非负整数,可能你有点晕,我们看几个例子。mex{1,2,3}=0;为什么?因为自然数从0开始,不属于这个集合最小的非负整数就是0了,再例如mex{0,1,3}=2;根据上面的观点这个应该也是很好理解的吧。
3.元素x的后继状态:这个可能比较难以理解,什么叫做元素x的后继状态呢?其实就是x可以转移过去的状态。我们在Bash博弈中,可以取1-m个石子,当前有x个石子,所以x可以转移到的状态是{x-1,x-2,....x-m}这个集合的每一个元素叫做x的后继状态。
4.SG函数:对于任意的状态x,我们定义函数SG(x)=mex(S),其中S的X的后继状态的集合。
5.SG定理:游戏和的SG函数等于各个游戏SG函数的Nim和。(NIm和就是每个SG函数值异或起来)。
6.SG函数值和博弈结果的关系:(1)在一个游戏中有n个物品,如果SG(n)=0,先手必败,否则先手必胜。(2)n个游戏组成的一个游戏(可以看做n堆物品),游戏和的SG函数等于0,先手必败,否则先手必胜。
7.实际的例子:SG函数的使用。这个例子不像传统的四大博弈一样直接有结论可用,其实这个有点像Nim博弈,但是拿去物品数量的限制使得这个题不能使用Nim博弈的结论,所以这个时候SG函数就十分有用了。我们先简单的分析一下SG函数的使用步骤
1、使用 数组S将 可改变当前状态 的方式记录下来。(其实就是可以拿取多少物品的集合)
2、然后我们使用 另一个数组 将当前状态x 的后继状态标记。
3、最后模拟mex运算,也就是我们在标记值中 搜索 未被标记值 的最小值,将其赋值给SG(x)。
4、我们不断的重复 2 - 3 的步骤,就完成了 计算1~n 的函数值。
下面是AC的代码:
#include
#define MAXN 1000 + 10
#define N 20
int f[N], SG[MAXN], S[MAXN];
void getSG(int n) {
int i, j;
memset(SG, 0, sizeof(SG));
for (i = 1; i <= n; i++)
{
memset(S, 0, sizeof(S));
for (j = 0; f[j] <= i && j < N; j++)
S[SG[i - f[j]]] = 1;
for (j = 0;; j++) if (!S[j])
{
SG[i] = j;
break;
}
}
}
int main() {
int n, m, k;
f[0] = f[1] = 1;
for (int i = 2; i <= 16; i++)
f[i] = f[i - 1] + f[i - 2];
getSG(1000);
while (scanf("%d%d%d", &m, &n, &k), m || n || k) {
if (SG[n] ^ SG[m] ^ SG[k]) printf("Fibo\n");
else printf("Nacci\n");
}
return 0;
}