解题模型:
1.把原游戏分解成多个独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或。
即SG(G)=SG(G1)^ SG(G2)^ … ^Sg(Gn)。
2.分别考虑每一个子游戏,计算其SG值。
SG值的计算方法:(重点)
a.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1)(Bash game)。
b.可选步数为任意步,SG(x) = x(Nim game)。
c.可选步数为一系列不连续的数,用模板计算。
结论:
1.当SG[x] = 0时,x为必败状态。
2.当SG[x] > 0时,x为必胜状态。
int f[N],SG[MAXN],S[MAXN];//f[] - 可改变当前状态 的方式 S[] - 当前状态的后继状态集合
//打表
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;//S[]数组来保存当前状态的后继状态集合
for(j = 0;; j++) if(!S[j]) {//模拟mex运算
SG[i] = j;
break;
}
}
}
//注意 f数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//n是集合f的大小 f[i]是定义的特殊取法规则的数组
int f[110],SG[10010],n;
int SG_dfs(int x)
{
int i;
if(SG[x]!=-1)
return SG[x];
bool vis[110];
memset(vis,0,sizeof(vis));
for(i=0;i<n;i++)
{
if(x>=f[i])
{
SG_dfs(x-f[i]);
vis[SG[x-f[i]]]=1;
}
}
int e;
for(i=0;;i++)
if(!vis[i])
{
e=i;
break;
}
return SG[x]=e;
}