描述
今天我們要認識一對新朋友,Alice與Bob。
Alice與Bob總是在進行各種各樣的比試,今天他們在玩一個取石子的游戲。
在這個游戲中,Alice和Bob放置了N堆不同的石子,編號1..N,第i堆中有A[i]個石子。
每一次行動,Alice和Bob可以選擇從一堆石子中取出任意數量的石子。至少取1顆,至多取出這一堆剩下的所有石子。
Alice和Bob輪流行動,取走最后一個石子的人獲得勝利。
假設每一輪游戲都是Alice先行動,請你判斷在給定的情況下,如果雙方都足夠聰明,誰會獲得勝利?
輸入
第1行:1個整數N。表示石子堆數。1≤N≤100
第2行:N個整數,第i個整數表示第i堆石子的個數A[i],1≤A[i]≤10000
輸出
第1行:1個字符串,若Alice能夠獲勝輸出"Alice",否則輸出"Bob"
- 樣例輸入
-
3 3 2 1
- 樣例輸出
-
Bob
這一次我們講的是一個古老而又經典的博弈問題:Nim游戲。
Nim游戲是經典的公平組合游戲(ICG),對於ICG游戲我們有如下定義:
1、兩名選手;
2、兩名選手輪流行動,每一次行動可以在有限合法操作集合中選擇一個;
3、游戲的任何一種可能的局面(position),合法操作集合只取決於這個局面本身;局面的改變稱為“移動”(move)。
4、若輪到某位選手時,該選手的合法操作集合為空,則這名選手判負。
對於第三條,我們有更進一步的定義Position,我們將Position分為兩類:
P-position:在當前的局面下,先手必敗。
N-position:在當前的局面下,先手必勝。
他們有如下性質:
1.合法操作集合為空的局面是P-position;
2.可以移動到P-position的局面是N-position;
3.所有移動都只能到N-position的局面是P-position。
在這個游戲中,我們已經知道A[] = {0,0,...,0}的局面是P局面,那么我們可以通過反向枚舉來推導出所有的可能局面,總共的狀態數量為A[1]*A[2]*...*A[N]。並且每一次的狀態轉移很多。
雖然耗時巨大,但確實是一個可行方法。
當然,我們這里會講這個題目就說明肯定沒那么復雜。沒錯,對於這個游戲有一個非常神奇的結論:
對於一個局面,當且僅當A[1] xor A[2] xor ... xor A[N] = 0時,該局面為P局面。
對於這個結論的證明如下:
1. 全0狀態為P局面,即A[i]=0,則A[1] xor A[2] xor ... xor A[N] = 0。
2. 從任意一個A[1] xor A[2] xor ... xor A[N] = k != 0的狀態可以移動到A[1] xor A[2] xor ... xor A[N] = 0的狀態。由於xor計算的特殊性,我們知道一定有一個A[i]最高位與k最高位的1是相同的,那么必然有A[i] xor k < A[i]的,所以我們可以通過改變A[i]的值為A[i]',使得A[1] xor A[2] xor ... xor A[i]' xor ... xor A[N] = 0。
3. 對於任意一個局面,若A[1] xor A[2] xor ... xor A[N] = 0,則不存在任何一個移動可以使得新的局面A[1] xor A[2] xor ... xor A[N] != 0。由於xor計算的特殊性,我們可以知道,一定是存在偶數個1時該位置的1才會被消除。若只改變一個A[i],無論如何都會使得1的數量發生變化,從而導致A[1] xor A[2] xor ... xor A[N] != 0。
以上三條滿足ICG游戲中N,P局面的轉移性質,所以該結論的正確性也得到了證明。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int n, p, a; 5 6 int main() { 7 while (cin >> n) { 8 for (int i = 0; i < n; ++i) { 9 cin >> a; 10 if (i == 0) p = a; 11 else p ^= a; 12 } 13 if (p == 0) cout << "Bob" << endl; 14 else cout << "Alice" << endl; 15 } 16 return 0; 17 }