首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数g如下:g(x)=mex{ g(y) | y是x的后继 },这里的g(x)即sg[x]
例如:取石子问题,有1堆n个的石子,每次只能取{1,3,4}个石子,先取完石子者胜利,那么各个数的SG值为多少?
sg[0]=0,f[]={1,3,4},
x=1时,可以取走1-f{1}个石子,剩余{0}个,mex{sg[0]}={0},故sg[1]=1;
x=2时,可以取走2-f{1}个石子,剩余{1}个,mex{sg[1]}={1},故sg[2]=0;
x=3时,可以取走3-f{1,3}个石子,剩余{2,0}个,mex{sg[2],sg[0]}={0,0},故sg[3]=1;
x=4时,可以取走4-f{1,3,4}个石子,剩余{3,1,0}个,mex{sg[3],sg[1],sg[0]}={1,1,0},故sg[4]=2;
x=5时,可以取走5-f{1,3,4}个石子,剩余{4,2,1}个,mex{sg[4],sg[2],sg[1]}={2,0,1},故sg[5]=3;
以此类推.....
x 0 1 2 3 4 5 6 7 8....
sg[x] 0 1 0 1 2 3 2 0 1....
一、Nim游戏的规则就是:每次选择一堆数量为k的石子,可以把它变成0、变成1、……、变成k-1,但绝对不能保持k不变。
二、定义有向图游戏的和(Sum of Graph Games):设G1、G2、……、Gn是n个有向图游戏,定义游戏G是G1、G2、……、Gn的和(Sum),游戏G的移动规则是:任选一个子游戏Gi 并移动上面的棋子。Sprague-Grundy Theorem就是:g(G)=g(G1)^g(G2)^…^g(Gn)。也就是说,游戏的和的SG函数值是它的所有子游戏的SG函数值的异或。
HDU 1848 取石子问题,一共有3堆石子,每次只能取斐波那契数个石子,先取完石子者胜利,问先手胜还是后手胜
const int maxn = 1000 ; int sg[maxn+8] ; vector <int> fibo ; int dfs(int n){ if(sg[n] != -1) return sg[n] ; bool vis[maxn + 8] ; memset(vis , 0 , sizeof(vis)) ; for(int i = 0 ; i < fibo.size() ; i++){ if(n - fibo[i] >=0) vis[dfs(n-fibo[i])] = 1 ; } int s = 0 ; while(vis[s]) s++ ; return sg[n] = s ; } int main(){ fibo.clear() ; fibo.push_back(1) ; fibo.push_back(2) ; int n , f , a , b , c ; for(n = 2 ; ; n++){ f = fibo[n-1] + fibo[n-2] ; if(f > 1000) break ; fibo.push_back(f) ; } memset(sg , -1 , sizeof(sg)) ; sg[0] = 0 ; for(n = 1 ; n <= 1000 ; n++) sg[n] = dfs(n) ; while(scanf("%d%d%d" ,&a ,&b ,&c)){ if(a==0 && b==0 && c==0) break ; if(sg[a] ^ sg[b] ^ sg[c]) puts("Fibo") ; else puts("Nacci") ; } return 0 ; }
HDU 1536
若存在移动某堆能到达一个必败点,则该点为必胜点,输出W
必败点指无论怎么移动都只能到达必胜点,输出L。
const int maxn = 10000 ; int sg[maxn+8] ; vector <int> lis ; int dfs(int n){ if(sg[n] != -1) return sg[n] ; bool vis[maxn + 8] ; memset(vis , 0 , sizeof(vis)) ; for(int i = 0 ; i < lis.size() ; i++){ if(n - lis[i] >=0) vis[dfs(n-lis[i])] = 1 ; } int s = 0 ; while(vis[s]) s++ ; return sg[n] = s ; } int main(){ // freopen("A.txt" , "r" , stdin) ; int k , i , x , m , s , n ; while(cin>>k && k){ lis.clear() ; for(i = 0 ; i < k ; i++){ scanf("%d" , &x) ; lis.push_back(x) ; } sort(lis.begin() , lis.end()) ; memset(sg , -1 , sizeof(sg)) ; sg[0] = 0 ; for(n = 1 ; n <= 10000 ; n++) sg[n] = dfs(n) ; cin>>m ; while(m--){ s = 0 ; scanf("%d" ,&n) ; for(i = 1 ; i <= n ; i++){ scanf("%d" ,&x) ; s ^= sg[x] ; } if(s) putchar('W') ; else putchar('L') ; } puts("") ; } return 0 ; }