基本概念
首先定义 (mex) (minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如 (mex{0,1,2,4}=3) 、(mex{2,3,5}=0)、(mex{}=0) 。
对于任意状态 (x) ,定义 (SG(x) = mex(F)) ,其中 (F) 是 (x) 后继状态的 (SG) 函数值的集合(就是上述 (mex) 中的数值)。例如 (x) 有三个后继状态分别为 (SG(a)) 、(SG(b)) 、(SG(c)) ,那么 (SG(x) = mex{SG(a),SG(b),SG(c)}) 。当且仅当 (x) 为必败点时,(SG(x)=0) 。
游戏和的 (SG) 函数等于各个游戏 (SG) 函数的 (Nim) 和。
取石子问题
有 (1) 堆 (n) 个石子,每次操作只能取 ({ 1, 3, 4}) 个石子,先取完石子者胜利,那么各个数的 (SG) 值为多少?
初始:(SG[0] = 0),(,f[]={1,3,4}) 。
(x=1) 时:可以取走 (1 - f{1}) 个石子,剩余 ({0}) 个,所以 (SG[1] = mex{ SG[0] }= mex{0} = 1) ;
(x=2) 时:可以取走 (2 - f{1}) 个石子,剩余 ({1}) 个,所以 (SG[2] = mex{ SG[1] }= mex{1} = 0) ;
(x=3) 时:可以取走 (3 - f{1,3}) 个石子,剩余 ({2,0}) 个,所以 (SG[3] = mex{SG[2],SG[0]} = mex{0,0} =1) ;
(x=4) 时:可以取走 (4- f{1,3,4}) 个石子,剩余 ({3,1,0}) 个,所以 (SG[4] = mex{SG[3],SG[1],SG[0]} = mex{1,1,0} = 2) ;
(x=5) 时:可以取走 (5 - f{1,3,4}) 个石子,剩余 ({4,2,1}) 个,所以 (SG[5] = mex{SG[4],SG[2],SG[1]} =mex{2,0,1} = 3) ;
以此类推。
SG函数打表模板
//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
int f[N], SG[maxn], S[maxn];
void getSG(int n){
int i, j;
memset(SG, 0, sizeof(SG));
//因为SG[0]始终等于0,所以i从1开始
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; //将后继状态的SG函数值进行标记
for(j = 0; ; j++) if(!S[j]){ //查询当前后继状态SG值中最小的非零值
SG[i] = j;
break;
}
}
}