版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Kamisama123/article/details/77649118

今天考了一道博弈论的题,让我重新复习一下SG定理吧。
首先通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。

我们知道对于最普通最一般的NIM取石子来说,所有石子个数异或起来如果为0,那么先手必败,如果不为0那么先手必胜。

具体证明如下:

1、最后的游戏状态必然是所有石子个数为0,也可以看成所有石子异或为0。
2、假如现在所有石子异或起来为J,我们用Ai表示第i堆石子个数,总共n堆。那么就有A1^A2^A3……^An=j,我们把j二进制表示,则A中必有一个数最高位与j的最高位同为1(位数一样),不然j的那个1是哪来的?然后我们令这个数是Am,所以Am^j必然小于Am,显然最高位被消掉变小了。我们可以取石子把Am变成Am^j,那么对于原式就是A1^A2^……^An^j=j^j=0;
3、如果异或起来为0,那么如果任意拿掉石子,新的异或一定不为0,这个证明十分简单,留给读者解决。(偷懒~)

所以当异或不为0的时候,都可以通过操作让它变成0,所以显然就有了上面那个结论。

那么现在我们上重头。

对于一个大的游戏,我们把它看成是多个小的游戏,比如这个取石子,我们把它看成是n个小的游戏。
现在我们单独考虑一个游戏,也就是一堆石子如何解决。
我们规定一个对于集合的操作mex,表示最小的不属于该集合的非负整数。
举几个栗子:mex{0,1,2}=3,mex{1,2,3}=0,mex{0,1,3}=2;

我们再定义SG函数:SG(x)=mex{ SG(y) | y是x的后继 }。
什么叫y是x的后继?我们用普通的NIM游戏举个栗子。假如有一堆石子个数为x,普通的NIM游戏可以取任意个,但不能一个不取,所以x的后继就是0~x-1,所以y取值0~x-1。
有一个结论就是普通的取石子SG(x)=x。

我们考虑SG函数的性质,我们把游戏看成一个图,那么其实就是一个点在这张图上面跑,当这个点走到0的时候它就走不动了。SG值为0时,表示当前点为必败点,因为它下面的SG值都大于0。SG值大于0,当前点是必胜点,因为它可以走到一个点的SG值为0。
所以得到结论SG值为0就是必败,SG大于0就是必胜。

我们已经考虑完了单个游戏,那么我们考虑把游戏合起来考虑。
合起来的游戏其实相当于n个点同时在这张图上面跑。
这里还有一个SG函数的性质,如果SG(x)=y,那么它可以走到的后继的SG值有0-y-1,参考它的定义。这里是不是有点熟悉?这不就相当于最普通的NIM取石子吗?可以取任意个,但不能一个不取。我们把SG值看成石子个数,那么整个游戏的SG值就是子游戏的SG值异或的和。

至此我们可以得到对于这类题,普遍的解法了。先打出SG函数找规律然后异或就完了。

给出一个找SG函数的模板:(其实就是模拟SG函数的定义)

int f[N],SG[N],S[N];  
void  getSG(int n){  
    int i,j;
    memset(SG,0,sizeof(SG));  
    for(i = 1; i <= n; i++){  
        memset(S,0,sizeof(S));  
        for(j = 0; f[j] <= i && j <= N; j++)  
            S[SG[i-f[j]]] = 1;    
        for(j = 0;; j++) if(!S[j]){ 
            SG[i] = j;  
            break;  
        }  
    }  
}  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
					<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-a47e74522c.css" rel="stylesheet">
            </div>