算法介紹:
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);
}
}
參考: