Nim-sum原理及應用

算法介紹:

Nim遊戲是指兩個對手在m個堆中輪流隨意從某一個堆中拿出n個元素,假定兩個對手都是足夠聰明,直至最後一次取的人將所有元素取出,此人取得勝利。與之相反的是Misere遊戲,相同的遊戲規則,但是最後一次取的人將落敗。

爲了解決這個問題,有一個叫做nim-sum的方法加以解決,這個方法是這樣的

設有三個堆分別是 Heap A, Heap B,Heap C,每個堆分別有8,12,13個元素

1)將每個堆的元素使用二進制表示,分別是1000,1100,1101

2)對三個數進行異或操作,即:

1000
1100
1101
-------
1001

就是十進制的9,這個就是三個堆的nim-sum,如果nim-sum爲0,則先手者不可能勝出

3)使用這個計算出來的nim-sum再次分別於三個堆中元素個數進行異或操作,如果得到異或的結果小於堆數則爲可選的必勝的操作,即:

情況一:

1000
1001
-------
0001<1000,可以爲必勝操作,此時先手者可以從Heap A中取出8(1000)-1(0001)=7個元素,則下一步的nim-sum爲0,接下來的策略就是依照這個算法繼續進行,模擬操作如下:

HeapA  HeapB  HeapC Nim-Sum

8              12           13             9           先手者從Heap A中拿出7個元素,使得下一步的nim-sum爲0,則先手者勝出

1              12            13            0           後手者從Heap B中拿出5個元素

1               7             13            11          先手者從Heap C中取出13-(13^11)=7個元素

1               7               6              0         後手者從Heap B中取出5個元素

1               2               6              5         先手者從Heap C中取出6-(6^5)=3個元素

1               2               3             0          後手者從Heap C中取出3個元素

1               2               0             3          到這一步,如果是nim遊戲,則在HeapB中取出1個元素(如果是misere遊戲,則全取HeapB所有元素)

1               1               0             0          後手者取出HeapA中一個元素

0               1               0              1         先手者取出HeapB中最後一個元素,先手者勝出

情況二:

1100
1001
-------
0101<1100,可以爲必勝操作,此時先手者可以從Heap B中取出12(1100)-5(0101)=7個元素

情況三:

1101
1001
-------
0100<1101,可以爲必勝操作,此時先手者可以從Heap C中取出13(1101)-4(010)=9個元素

分析:

如上面8 12 13

對情況一,三個堆可分解爲

HeapA    1    7

HeapB   12

HeapC   12   1

多出元素爲HeapA中的7,取出後三個堆呈現對稱分佈

對情況二,三個堆可分解爲

HeapA     8

HeapB     5      7

HeapC     5      8

多出元素爲HeapB中的7,取出後三個堆呈現對稱分佈

對情況三,三個堆可分解爲

HeapA    8

HeapB    8     4

HeapC    4     9

多出元素爲HeapC中的9,取出後三個堆呈現對稱分佈

  

總結:

1.可以通過計算所有堆的nim-sum得出先手者是否可以取勝,如果不是0則可以,爲0則不可以

2.可以用計算後的nim-sum分別與所有堆的元素進行異或操作,如果得到結果小於原來堆的元素,則爲可選操作

 

例子:

以hdoj中1850爲例:http://acm.hdu.edu.cn/showproblem.php?pid=1850,代碼如下:

#include<stdio.h>


void main(){
	int m,a[101]={0},nim,result;
	while(1){
		nim = 0;
		result =0;
		scanf("%d",&m);
		if(m==0)
			break;
		for(int i=1; i<=m; i++){
			scanf("%d",&a[i]);
			nim = nim^a[i];
		}
		if(nim ==0){
			printf("0\n");
			break;
		}
		for(int j=1; j<=m; j++){
			if((nim^a[j])<a[j])
				result++;
		}
		printf("%d\n",result);
	}
}

 

參考:

http://en.wikipedia.org/wiki/Nim
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章