1. 程式人生 > >hdu 1850 Being a Good Boy in Spring Festival(尼姆博弈)

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;
}

執行結果:
這裡寫圖片描述