hdu 1850 Being a Good Boy in Spring Festival(尼姆博弈)
Being a Good Boy in Spring Festival
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Problem Description
一年在外 父母時刻牽掛
春節回家 你能做幾天好孩子嗎
寒假裡嘗試做做下面的事情吧
陪媽媽逛一次菜場
悄悄給爸爸買個小禮物
主動地 強烈地 要求洗一次碗
某一天早起 給爸媽用心地做回早餐
如果願意 你還可以和爸媽說
咱們玩個小遊戲吧 ACM課上學的呢~
下面是一個二人小遊戲:桌子上有M堆撲克牌;每堆牌的數量分別為Ni(i = 1…M);兩人輪流進行;每走一步可以任意選擇一堆並取走其中的任意張牌;桌子上的撲克全部取光,則遊戲結束;最後一次取牌的人為勝者。
現在我們不想研究到底先手為勝還是為負,我只想問大家:
——“先手的人如果想贏,第一步有幾種選擇呢?”
Input
輸入資料包含多個測試用例,每個測試用例佔2行,首先一行包含一個整數M(1 < M <= 100),表示撲克牌的堆數,緊接著一行包含M個整數Ni(1 <= Ni <= 1000000,i = 1…M),分別表示M堆撲克的數量。M為0則表示輸入資料的結束。
Output
如果先手的人能贏,請輸出他第一步可行的方案數,否則請輸出0,每個例項的輸出佔一行。
Sample Input
3
5 7 9
0
Sample Output
1
此題中ans = g[arr[0]] ^ g[arr[1]] ^ g[arr[2]] ^ …^ g[arr[i]] ^ … ^ g[arr[M - 1]],
ans == 0時先手必敗,因此只能輸出0,
ans != 0時,ans ^ g[arr[i]] == g[arr[0]] ^ g[arr[1]] ^ g[arr[2]] ^ …^ g[arr[i - 1]] ^ g[arr[i + 1]] ^ … ^ g[arr[M - 1]],
只需使原ans中的g[a[i]] = ans ^ g[a[i]],就可以使
ans = g[arr[0]] ^ g[arr[1]] ^ g[arr[2]] ^ …^ g[arr[i - 1]] ^ g[arr[i + 1]] ^ … ^ g[arr[M - 1]] ^ (g[arr[0]] ^ g[arr[1]] ^ g[arr[2]] ^ …^ g[arr[i - 1]] ^ g[arr[i + 1]] ^ … ^ g[arr[M - 1]])
= (g[arr[0]] ^ g[arr[0]]) ^ (g[arr[1]] ^ g[arr[1]]) ^ (g[arr[2]] ^ g[arr[2]]) ^…^ (g[arr[i - 1]] ^ g[arr[i - 1]]) ^ (g[arr[i + 1]] ^ g[arr[i + 1]]) ^ … ^ (g[arr[M - 1]] ^ g[arr[M - 1]])
= 0,
此時後手處於奇異局勢(後手必敗),說明這個策略是可選的。
注意ans ^ g[arr[i]]必須保證小於arr[i],若前者大於後者,在這道題中,g[arr[i]] == arr[i],若是改變反而相當於要加牌進去了;若前者與後者相等,就相當於沒有做任何操作。這兩種情況明顯都是不合法的。
PS:
我在一些這道題的題解裡看到與這段話意思相近的話:
對於某個局面(a1,a2,…,an),若a1^a2^…^an!=0,一定存在某個合法的移動,將ai改變成ai’後滿足a1^a2^…^ai’^…^an=0。不妨設a1^a2^…^an=k,則一定存在某個ai,它的二進位制表示在k的最高位上是1(否則k的最高位那個1是怎麼得到的)。這時ai^k < ai一定成立。則我們可以將ai改變成ai’=ai^k,此時a1^a2^…^ai’^…^an=a1^a2^…^an^k=0.
起初我一直沒有看懂Orz,直到看到了這個:
【轉】博弈論(一)
上面那段話是這篇文章裡的一段原話(雖不知是否最初出自這裡),這一段的原意是要證明以下命題的第二個:
證明一種判斷position的性質的方法的正確性,只需證明三個命題:
1、這個判斷將所有terminal position判為P-position;
2、根據這個判斷被判為N-position的局面一定可以移動到某個P-position;
3、根據這個判斷被判為P-position的局面無法移動到某個P-position。
第二個命題稍微轉換一下就是在問:
能夠使一個非奇異局勢轉變為一個奇異局勢的移動操作的存在性。
上面引用的證明過程中ai ^ k < ai這個條件是k最高位的1來自於ai時才成立的,後者是前者的充分條件,沒有給出必要性的證明。
條件為k最高位的1來自於ai,那麼ai ^ k進行運算時,有兩種情況:
1.ai的最高位1就是k的最高位1,由於2 ^ n > 2 ^ (n - 1) + 2 ^ (n - 2) + …… + 2 + 1,毫無疑問ai ^ k < ai;
2.ai的最高位1高於k的最高位1,此時ai的最高位1到k的最高位1之間不會發生變化,ai的提供k最高位1的1會變成0,但由於同上的原因,無論之後的n - 1位發生什麼樣的變化,結果都會是ai ^ k < ai.
因此得出結論:當且僅當ai ^ k < ai時,k最高位的1來自於ai,取ai’ = ai ^ k替換原式中的ai時,能夠使一個非奇異局勢轉變為一個奇異局勢。
這個證明是自己想的,數學證明一直是短板,難免有疏漏,還請各位反饋,我會及時訂正(當然前提是有人看的話Orz)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define MAX 1000005
#define MM 105
#define N 5
using namespace std;
int arr[MM];
int g[MAX];
int cnt;
void SG()//這個sg函式不寫也行,只是個形式
{
for(int i = 0; i < MAX; i++) g[i] = i;
}
int solve(int ans, int b)//同上
{
return ans ^ g[b];
}
int main()
{
SG();
int M;
while(scanf("%d", &M) && M)
{
cnt = 0;
scanf("%d %d", &arr[0], &arr[1]);
int ans = g[arr[0]] ^ g[arr[1]];
for(int i = 2; i < M; i++)
{
scanf("%d", &arr[i]);
ans = solve(ans, arr[i]);
}
if(ans)
{
for(int i = 0; i < M; i++)
{
if((ans ^ arr[i]) < arr[i])//注意優先順序
cnt++;
}
printf("%d\n", cnt);
}
else
printf("0\n");
}
return 0;
}
執行結果: