组合游戏之基础博弈:http://blog.csdn.net/m0_37345402/article/details/77198270
Sprague-Grundy(SG)
SG值: 除 任意一步所能转移到的子局面的SG值以外的最小非负整数。
SG定理:
游戏和的SG函数等于各个游戏SG函数的Nim和。假如说在一个游戏中有多个石子堆,我们只需要把对每个石子堆进行sg函数的调用,将得到的所有的值进行异或。得出来的结果为0则局面为必败态。否则为必胜态。
SG函数:
可以将sg函数看作是一个深搜的的过程。而每一堆的石子就相当于图中间的节点,(状态为点,决策为边)所以说整个sg函数的过程就是在对一个有向无环图进行dfs的过程。终止状态出度为0.
定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如 mex{}=0,mex{0,1,2,4}=3,mex{2,3,5}=0。
sg[x] =mex{sg[y] | y是x的后继} y是x的后继,即x下一步可以到达的状态y
eg.取石子问题
一堆n个的石子,每次只能取{1,3,4}个石子,先取完石子者获胜,求各个数的sg值。
sg[0]=0, f[]={1,3,4}
n=1时,可以取走1 - f{1}个石子,剩余{0}个,所以 sg[1] = mex{ sg[0] }= mex{0} = 1;
n=2,可以取走2 - f{1}个石子,剩余{1}个,所以 sg[2] = mex{ sg[1] }= mex{1} = 0;
n=3,可以取走3 - f{1,3}个石子,剩余{2,0}个,所以 sg[3] = mex{sg[2],sg[0]} = mex{0,0} =1;
n=4,可以取走4 - f{1,3,4}个石子,剩余{3,1,0}个,所以 sg[4] = mex{sg[3],sg[1],sg[0]} = mex{1,1,0} = 2;
n=5,可以取走5 - f{1,3,4}个石子,剩余{4,2,1}个,所以sg[5] = mex{sg[4],sg[2],sg[1]} =mex{2,0,1} = 3;
.....
x 0 1 2 3 4 5 6 7 8....
sg[x] 0 1 0 1 2 3 2 0 1....
打表模板
void get_sg()
{
memset(sg,0,sizeof(sg));
int i,j;
for(i=1;i<=n;i++)//sg[0]=0,所以i从1开始,打表1-n个石子。
{
memset(math,0,sizeof(math));//math[]保存所有后继值
//a[t]表示改变当前状态的方式,即每次可取的值,t为方式的种类
for(j=0;a[j]<=i&&j<=t;j++)
{
math[sg[i-a[j]]]=1;//把后继状态的sg函数值标记为1
}
for(j=0;j<=n;j++) //模拟mex运算
{
if(math[j]==0)//查询后继状态sg值中最小的非零值
{
sg[i]=j;
break;
}
}
}
}
dfs 递归模板
int get_sg(int x)
{
int i;
if(sg[x]!=-1)
return sg[x];
bool math[10005];//定义在里面!
memset(math,0,sizeof(math));
for(i=0;i<t&&f[i]<=x;i++)
{
get_sg(x-f[i]);
math[sg[x-f[i]]]=1;
}
for(i=0;;i++)
{
if(math[i]==0)
{
sg[x]=i;
break;
}
}
return sg[x];
}