SG函数的定义:
g(x) = mex ( sg(y) |y是x的后继结点 )
其中mex(x)(x是一个自然是集合)函数是x关于自然数集合的补集中的最小值,比如x={0,1,2,4,6} 则mex(x)=3;
什么是后继结点?
所谓后继结点就是当前结点经过一个操作可以变成的状态。比如对于娶4石子游戏,假如每次可以取的数目是1,2,4,当前的石子数目也就是当前状态是5,那么5的后继结点就是{5-1, 5-2, 5-4}={4,3,1};
如果5的三个后继结点的SG函数值分别为0,1,3,那么5的SG值就是集合{0,1,3}的补集的最小元素,也就是2。
关于整个游戏的sg值之和sum,定义sum=sg1 ^ sg2 ^ sg3 ^ ……sgn. 其中^表示按位异或运算。
结论:一个游戏的初始局面是必败态当且仅当sum=0。
一篇非常好的关于SG值的论文:http://www.cnitblog.com/weiweibbs/articles/42735.html
SG值打表模板:
//f[]:可以取走的石子个数 //sg[]:0~n的SG函数值 //hash[]:mex{} int f[N],sg[N],hash[N]; void getSG(int n) { int i,j; memset(sg,0,sizeof(sg)); for(i=1;i<=n;i++) { memset(hash,0,sizeof(hash)); for(j=1;f[j]<=i;j++) hash[sg[i-f[j]]]=1; for(j=0;j<=n;j++) //求mes{}中未出现的最小的非负整数 { if(hash[j]==0) { sg[i]=j; break; } } } }
HDU1848
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1848
题意:取石子问题,一共有3堆石子,每次只能取斐波那契数个石子,先取完石子者胜利,问先手胜还是后手胜
- 可选步数为一系列不连续的数,用GetSG(计算)
- 最终结果是所有SG值异或的结果
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=1001; //f[] 可以取走的石子数 //sg[] 0~n的sg函数值 //hash[] mex{} int f[maxn],sg[maxn],hash[maxn]; void getsg(int n) { memset(sg,0,sizeof(sg)); for(int i=1;i<=n;i++) { memset(hash,0,sizeof(hash)); for(int j=1;f[j]<=i;j++) hash[sg[i-f[j]]]=1; for(int j=0;j<=n;j++) { if(hash[j]==0) { 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(cin>>n>>m>>k) { if(!n&&!m&&!k) break; int sum=0; sum=sg[n]^sg[m]^sg[k]; if(sum==0) cout<<"Nacci"<<endl; else cout<<"Fibo"<<endl; } return 0; }