POJ 2960 S-Nim【SG函数的应用】


http://poj.org/problem?id=2960
POJ 2960 S-Nim
大意:有n堆石子,每堆石子个数已知,两人轮流从中取石子,
每次可取的石子数x满足x属于集合S(k) = {s1,s2,s3...sk-1},问先拿者是否有必胜策略?
分析:
 1.可将问题转化为n个子问题,每个子问题分别为:
   从一堆x颗石子中取石子,每次可取的石子数为集合S(k)中的一个数
 2.分析(1)中的每个子问题,
   易得:SG(x) = mex(SG[x-s[i]])(0<i<k-1);
 3.后面就是SG函数的应用,根据Sprague-Grundy Therem:g(G)=g(G1)^g(G2)^g(G3)^...^g(Gn)
   即游戏的和的SG函数值是它的所有子游戏的SG函数值的异或,即
   SG(G) = SG(x1)^SG(x2)^...^SG(xn),故若SG(G)=0那么必输

View Code
   
     
1 #include < stdio.h >
2 #include < string .h >
3   const int N = 10001 ;
4 const int M = 101 ;
5 int SG[N]; // SG[i]记录一堆i颗石子的SG状态
6 int s[M]; // 存储可选取的石子数目集合
7 int k; // s[]集合中的元素个数
8
9 void DFS( int x) // 递归求解SG
10 {
11 if (SG[x] !=- 1 ) return ;
12 bool visited[N];
13 for ( int i = 0 ;i <= x;i ++ )
14 visited[i] = false ;
15 for ( int i = 0 ;i < k;i ++ )
16 {
17 int temp = x - s[i];
18 if (temp >= 0 )
19 {
20 if (SG[temp] ==- 1 )
21 DFS(temp);
22 visited[SG[temp]] = true ;
23 }
24 }
25 for ( int i = 0 ;i < N;i ++ )
26 if ( ! visited[i])
27 {
28 SG[x] = i;
29 return ;
30 }
31 }
32 int main()
33 {
34 while (scanf( " %d " , & k) != EOF) // 可选集合中的元素个数
35 {
36 if (k == 0 ) break ;
37 int i,ans;
38 for (i = 0 ;i < k;i ++ )
39 scanf( " %d " , & s[i]);
40 memset(SG, - 1 , sizeof (SG));
41 SG[ 0 ] = 0 ;
42 int m;
43 scanf( " %d " , & m); // 测试组数
44 while (m -- )
45 {
46 ans = 0 ;
47 int l;
48 scanf( " %d " , & l); // 石子堆数
49 while (l -- )
50 {
51 int x;
52 scanf( " %d " , & x); // 当前堆石子数目
53 if (SG[x] ==- 1 )
54 DFS(x);
55 ans = ans ^ SG[x];
56 }
57
58 if (ans == 0 )printf( " L " );
59 else
60 printf( " W " );
61 }
62
63 printf( " \n " );
64 }
65
66 return 0 ;
67 }

你可能感兴趣的:(poj)