SG函数与SG定理
SG函数
\(mex\)运算:\(mex\)运算是一个施加于集合的运算,表示最小的不属于这个集合的非负整数。例如\(mex\{0,1,3\}=2,mex\{1\}=0,mex\{\}=0\)
\(SG\)函数:对于任意状态\(x\),\(SG(x)=mex(\{SG(y)|\)\(y\)是\(x\)的后继状态\(\})\)
当\(SG(x)==0\)时\(x\)为必败态,\(SG(x)\neq 0\)时\(x\)为必胜态
\(SG\)函数的计算可以使用动态规划或者记忆化搜索,设状态数的最大值为\(n\),转移数为\(m\),则时间复杂度\(O(n*m)\)
模板:动态规划计算\([0,n]\)的SG函数值
int SG[maxn],S[maxn],f[maxm];
void get_SG(int n,int m){
int i,j;
memset(SG,0,sizeof(SG));
for(i=1;i<=n;i++){
memset(S,0,sizeof(S));
for(j=0;j<m && f[j]<=i;j++)
S[SG[i-f[j]]]=1;
j=0;
while(S[j]!=0) j++;
SG[i]=j;
}
}
相关题目:hdu1536 S-Nim
记忆化搜索计算\(SG\)函数,虽然每堆石子数最多为\(10000\),但是由于转移数最多为\(100\),所以\(SG\)函数的值不超过\(100\),可以开大小为\(110\)的\(S\)数组
#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define pi acos(-1.0)
#define lowbit(x) x&(-x)
using namespace std;
int number_s,s[110],T,number_a,a[110],SG[10010];
int f(int x){
if(SG[x]!=-1) return SG[x];
bool S[110];
for(int i=0;i<=100;i++) S[i]=0;
for(int i=1;i<=number_s && x-s[i]>=0;i++){
S[f(x-s[i])]=1;
}
int i=0;
while(S[i]) i++;
return SG[x]=i;
}
int main(void){
while(scanf("%d",&number_s)!=EOF && number_s){
for(int i=1;i<=number_s;i++){
scanf("%d",&s[i]);
}
sort(s+1,s+1+number_s);
scanf("%d",&T);
memset(SG,-1,sizeof(SG));
SG[0]=0;
while(T--){
scanf("%d",&number_a);
int ans=0;
for(int i=1;i<=number_a;i++){
scanf("%d",&a[i]);
ans^=f(a[i]);
}
if(ans) printf("W");
else printf("L");
}
printf("\n");
}
return 0;
}
SG定理
\(SG\)定理:游戏和的\(SG\)函数等于各个游戏的\(SG\)函数的异或和
\(SG\)函数表示除了后继状态\(SG\)函数值以外的最小值,也就是从\(SG(x)=k\)的状态可以转移到\(0,1,\cdots,k-1\)的所有状态。这和\(NIM\)博弈相同,所以可以通过异或和判断胜败。