博弈——SG函数模板

  之前遇到博弈的题老是无从下手,今天学习了下博弈题的一个“小套路”,叫做Sprague-Grundy函数

Sprague-Grundy函数定义为不出现在F(X)中每一个元素的Sprague-Grundy函数最小非负数。听着有点绕,其实举个例子就很好理解了。

举例子前先说几个名词:

  x:当前状态量

  F(x):表示一个点能到达点的集合

  SG[]:0-n的SG函数值

  S[]:x后继状态的集合

  mex{}:表示最小的不属于这个集合的非负整数

举个栗子:一堆n个的石头,每次只能取{1,3,5}个石头,先取完石头的赢。

  首先SG[0]=0是恒定不变的。

  x为1时,最多只能取1个石头了,取完之后剩0个,所以SG[1]=mex{SG[0]}=mex{0}=1;

  x为2时,最多只能取1个石头了,取完之后剩1个,SG[2]=mex{SG[1]}=mex{1}=0;

  x为3时,能取1个或3个,剩余2个或0个,SG[3]=mex{SG[0],SG[2]}=mex{0,0}=1;

  ...

以上,就搞清楚了SG函数的计算了,然后看看有什么用呢?

先打表看下0-9下对应的SG值

然后我们不难发现:

  当SG值为0时,该状态为必败局面。

  当SG值非0时,该状态为必胜局面。

这就很有用了!

上面说的是一堆石头,当有几堆石头时可以类推出这样的处理:(别问我怎么推的,我也不知道╮(╯▽╰)╭

  SG[n1]^SG[n2]^SG[n3]^....^SG[nx]

  然后对它进行真值判断。


上一道例题的代码 http://hdu.hustoj.com/showproblem.php?pid=1848

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 //SG打表
 4 const int N = 20;
 5 const int maxn = 1010;
 6 int F[N], S[maxn], SG[maxn];
 7 void getSG(int n)
 8 {
 9     int i, j;
10     memset(SG, 0, sizeof(SG));
11     for(i = 1; i <= n; i++)
12     {
13         memset(S, 0, sizeof(S));
14         for(j = 0; F[j] <= i && j <= N; j++)
15         {
16             S[SG[i - F[j]]] = 1;    //标记
17         }
18         for(j = 0;; j++)
19             if(!S[j])
20             {
21                 SG[i] = j;
22                 break;
23             }
24     }
25 }
26 int main()
27 {
28     F[0] = 1;
29     F[1] = 1;
30     F[2] = 2;
31     for(int i = 3; i <= 20; i++)
32         F[i] = F[i - 1] + F[i - 2];
33     getSG(1000);
34     int m, n, p;
35     while(cin >> m >> n >> p && m != 0 && n != 0  && p != 0)
36     {
37         /*
38         if(SG[m]^SG[n]^SG[p])
39             cout << "Fibo" << endl;
40         else
41             cout << "Nacci" << endl;
42         */
43         if((SG[m]^SG[n]^SG[p]) == 0)    //按位异或的优先级低于等值判定,怪不得交了几遍都是WA!!!
44             cout << "Nacci" << endl;
45         else
46             cout << "Fibo" << endl;
47         
48     }
49     return 0;
50 }

猜你喜欢

转载自www.cnblogs.com/friend-A/p/9141816.html