SG函数

首先定义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

题意:多组测试数据 ,输入 k个集合S的元素,m种情况,m种(L堆,每堆hi个)。

            若存在移动某堆能到达一个必败点,则该点为必胜点,输出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 ;
}




你可能感兴趣的:(SG函数)