之前遇到博弈的题老是无从下手,今天学习了下博弈题的一个“小套路”,叫做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 }