HDOJ 1536 S-Nim

             首先,S-Nim是经典的博弈类的题目可以用Sprague-Grundy函数进行解决,关于Sprague-Grundy函数的方法,见链接文章:Sprague-Grundy函数

             其次,关于Sprague-Grundy函数的SG值。S-Nim类的游戏转化为Nim游戏,并将每个点当做一堆石子,该点的SG值当做该堆的石子数。但是为什么SG值有什么意义?为什么可以转化为石子数?只有理解了SG值的确切意义才能完全明白为什么S-Nim向Nim的转化。看一下以下例子:

                                     SG=g(x)={0,1,2,3}=4      ............................................. ①

                                     SG=g(x)={0,1,2,3,5}=4   ..............................................②   

                                     SG=g(x)={1,2,3,5}=0   .................................................③

对比①和②可以发现,SG值似乎丢失了一些信息,②中的5值并没有通过SG值表示出来。对比②和③可以发现,③中信息似乎完全丢失了。但,是不是真的如此呢?回到博弈类题目的出发点:

 一、博弈题目依据什么推测最终的输赢?

        答:依据的是当前点处于必胜点,还是必败点。除此之外无需假借任何条件。

二、必胜点与必败点有什么性质。

        答:(1) 所有终结点是必败点(P点);

                (2) 从任何必胜点(N点)操作,至少有一种方法可以进入必败点(P点);

                (3)无论如何操作, 从必败点(P点)都只能进入必胜点(N点).

然后,看一下SG值,SG值为零的点,则为P点(即,必败点),否则为N点(即,必胜点)。以上SG值的性质满足必败点与必胜点的所有性质:(1)所有终结点的SG值为零,为必败点。(2)若SG值不为零,则其为必胜点。设其值为m,则有SG=g(x)={0,1,2,....m-1}=m,大括号中的0为其后继点的SG值,即该点为必败点。所以满足从必胜点至少有一种方法进入必败点。(3)若,SG值为零的点不是终结点,则必存在一个值为m的点,使得SG=g(x)={...,m,...},大括号中的m为其后继点的SG值,即该点为必胜点。所以满足从必败点都只能进入必胜点。

         综上所诉,SG值为零的点,则为P点(即,必败点),否则为N点(即,必胜点)。满足必胜点与必败点所有的性质。并且可以作为推测输赢的依据。例②中所谓的信息丢失并不存在,因为SG值仅仅关注零和非零,其重点在于后继点的SG值中是否存在零。例③中的信息同样并未丢失,因为其SG值为零恰恰表明了后继点中有不为零的值存在(或无后继点),而必败点仅仅关心这一点信息。所以SG值仅有两个意义,零和非零,标明了该点为必胜点还是必败点。因为石子的数量与SG值起到的相同的作用(标明必胜点或者必败点),因此S-Nim类题目转化为Nim类题目时可以将SG值与石子数量同等对待,更是因为如此S-Nim题目才可以转化为Nim类题目。

        有了以上转换S-Nim就不难搞定了,代码贴上来:

View Code
 1 //#include <fstream>   
2 #include <iostream>
3 #include <algorithm>
4 using namespace std;
5 int f[10001];
6 unsigned set[101];
7 int setNum;
8
9 int mex(int v)
10 {
11 bool g[101]={0};
12 for (int i=0;i<setNum;i++)
13 {
14 int t=v-set[i];
15 if(t<0)
16 break;
17 if(f[t]==-1)
18 f[t]=mex(t);
19 g[f[t]]=1;
20 }
21 for (int i=0;i<101;i++)
22 {
23 if(!g[i])
24 return i;
25 }
26 }
27 int main()
28 {
29 //ifstream cin("S-Nim.txt");
30 while(cin>>setNum,setNum)
31 {
32 for(int i=0;i<setNum;i++)
33 cin>>set[i];
34 sort(set,set+setNum);
35 memset(f,-1,sizeof(f));
36 f[0]=0;
37 int caseNum;
38 cin>>caseNum;
39 while(caseNum--)
40 {
41 int num;
42 cin>>num;
43 int s=0,value;
44 while(num--)
45 {
46 cin>>value;
47 if(f[value]==-1)
48 f[value]=mex(value);
49 s=s^f[value];
50 }
51 if(s==0)
52 cout<<"L";
53 else
54 cout<<"W";
55 }
56 cout<<endl;
57 }
58 }




 

 

posted on 2011-08-07 18:30  AdaByron  阅读(835)  评论(0编辑  收藏  举报

导航