CSDN英雄會-第五屆在線編程大賽月賽第三題:石子游戲(1)

題目詳情
甲乙兩人面對若干堆石子,其中每一堆石子的數目可以任意確定。
兩人輪流按下列規則取走一些石子,遊戲的規則如下:
1.每一步應取走至少一枚石子;
2.每一步只能從某一堆中取走部分或全部石子;
3.如果誰無法按規則取子,誰就是輸家。
如果甲乙兩人都採取最優的策略,甲先拿,請問,是甲必勝還是乙必勝.
輸入格式:
多組數據,每組數據兩行,第一行是一個整數N, 2<=N<=10000
下一行是N個正整數,代表每堆的石子數,石子數在32位整數內。
輸出格式:
每組測試數據輸出一行,如果甲存在必勝策略,輸出”Win”,否則輸出”Lost”
答題說明
輸入樣例
3
3 3 1
輸出樣例:
Win

這是一道很美妙的博弈論向的問題。(爲博弈論中經典的模型:Nim遊戲

首先以遞推的方式來看:
1、若僅僅存在一堆石子,那麼甲是可以勝的(直接全拿走就好了),由於遊戲中規定:甲乙兩人都採取最優的策略。那麼就說明在甲乙在這種前提下是必勝的。

2、若存在兩堆石子,我們開始討論:
對於[1,1]這樣的情況,甲必敗。
對於[n,1]這樣的情況,甲可以將[n,1]變爲[1,1],於是在這種情況下甲是必勝的。
而對於[n,m]這樣的情況,根據之前我們討論的,甲需要避免的是:在甲取完後不能爲[n,1],那麼如果是取爲[n,2]呢?
假定我們採取這樣的策略,同樣地[n,2]依舊屬於[n,m]類問題。採取這種策略必然會出現[2,2]這樣的情況。而對於面對[2,2]這種情況的玩家來說,則是必敗的。
以此類推[3,3]也是如此。繼而可以得出,若開局爲[n,n]類,則甲必敗,反之,若開局爲,[n,m]類,則甲必勝。(畢竟若m>n,則可以[n,m]=>[n,n],則乙必敗)

推演至n堆如何來做呢?
我們可以將每一大堆以相同的方式分爲若干小堆。即將n個大堆,表示爲n個k維向量,要求每一個向量的第i維與第j維(i不等於j)不相關(即對於任意的向量a,有a[i]=1 且 a[j]!=1,(i!=j)),每一維度值類型爲Boolean類型,表示爲該大堆在該維度上劃分的小堆是否存在。而後,判斷n個k維向量每一維存在的小堆個數,如果都爲偶數,則甲必敗(原因參照之前的討論)
具體來說,對於一組樣例8,11,2,14來說,這四個大堆可以表示爲四個四維向量,即將他們劃分爲1個石子爲一小堆、兩個石子爲一小堆、四個石子爲一小堆、八個石子爲一小堆的四種小堆。
08->[1,0,0,0]
11->[1,0,1,1]
02->[0,0,1,0]
14->[1,1,1,0]
由此可以得知,甲必勝。
樣例爲3,3,1 可以劃分爲二維向量
3->[1,1]
3->[1,1]
1->[0,1]
由此得知,甲必勝。

由於計算機中存儲的整數爲二進制,則可以將int型的石子數看作32維向量(題目中要求)且對於每一維來說,僅需要知道是否爲偶數,那麼可以通過位運算來解決。
於是我們需要設計出滿足要求的布爾函數式:

A B | Ans
——————————
0 0 | 0
0 1 | 1
1 0 | 1
0 0 | 0

於是可以推演出函數式:Ans=(~A & B) | (A & ~B)
其中,0代表該維度初始或經過計算後爲偶數個小堆,反之爲奇數個小堆。由於上述的劃分,若向量表示第i個小堆存在,則僅存在1個第i個小堆,即爲奇數個小堆。(這個運算很類似於不進位的加法運算)
而最終的結果是要看是否全部爲偶數,則僅僅需判斷累加器中的數是否爲0

代碼如下:

#include <stdio.h>
int main(){
    int n;
    while(scanf("%d", &n) != EOF){
        int s = 0;
        while(n--){
            int temp;
            scanf("%d", &temp);
            s = (~s & temp) | (s & ~temp);
        }
        s?printf("Win\n"):printf("Lost\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章