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 }