博弈遊戲·Nim遊戲·二

時間限制:10000ms
單點時限:1000ms
內存限制:256MB

描述

Alice和Bob這一次準備玩一個關於硬幣的遊戲:
N枚硬幣排成一列,有的正面朝上,有的背面朝上,從左到右依次編號爲1..N。現在兩人輪流翻硬幣,每次只能將一枚正面朝上的硬幣翻過來,並且可以隨自己的意願,在一枚硬幣翻轉後決定要不要將該硬幣左邊的任意一枚硬幣也翻一次(正面翻到背面或背面翻到正面)。翻最後一枚正面向上的硬幣的人獲勝。同樣的,這次遊戲裏面Alice仍然先手,兩人均採取最優的策略,對於給定的初始局面,Alice會獲勝還是Bob會獲勝?

提示:Turning Turtles

輸入

第1行:1個正整數N,表示硬幣數量。1≤N≤10,000
第2行:1個字符串,第i個字符表示編號爲i的硬幣狀態,’H’表示正面朝上,’T’表示背面朝上。

輸出

第1行:1個字符串,若Alice能夠獲勝輸出"Alice",否則輸出"Bob"

樣例輸入
8
HHTHTTHT
樣例輸出
Bob

## 題目解析:

在解答這道題之前,來先看另一道題。有若干堆糖果,Alice 和 Bob 兩個人,都可以從一堆糖果中取出至少一個糖,當然也可以把整堆糖一齊取走。取走最後一堆,或最後一顆糖,即對方無法再取糖時,則獲勝!問如何解決此問題。


我們假設有三堆糖,分別有12、5、13顆糖。用二進制表示如下:

1 1 0 0

0 1 0 1

1 1 0 1


此時 12 ^ 5 ^ 13 = 0 1 0 0,此時Alice只需要從任意一堆,比如第一堆取走4顆:

1 0 0 0

0 1 0 1

1 1 0 1

那麼此時,無論Bob如何從哪堆糖果中取走整堆,或取走若干糖果,其對應的效果即是 該堆的對應的二進制變成一個更小的數字。比如Bob從1 1 0 1取走3顆:

1 0 0 0

0 1 0 1

1 0 1 0

那麼Alice只需要從0 1 0 1也取走3顆,

1 0 0 0

0 0 1 0

1 0 1 0

即可使得三堆數字的 XOR 結果還爲0,按照這種策略下去,Alice一定可以贏。因此,先行者只需要將每堆的數字 XOR 之後,得到一個結果,如果不爲0,那麼先行者一定有辦法使得改變某一堆的大小,使得之後的所有堆的XOR值爲0,那麼先行者就贏。



那麼針對本文上面提出的題目,如何轉化該剛剛所敘述的題目呢?

比如本文的輸入 

HHTHTTHT

比如對於第7個H,翻轉該H時,可以有三種選擇:

1,直接只翻轉第7個H,不碰其他的H或T

2,翻轉第7個H,將其他更低位的某個H變成T

3,翻轉第7個H,將其他更低位的某個T變成H

其實可以歸爲如下操作:

將第7個H,轉變成第0個H(即對應上述操作1);

轉變成第1個H,並且該H與原先第1位上的H相互抵消,變成T(對應上面操作2);

轉變成第2個H,並且該H與原先第2位的H相互抵消,變成T(對應上面操作2);

轉變爲第3個H,並且該H與原先第3位的T相互結合,變成H(對應上面操作3)

... 依次類推


其實這就對應着剛剛敘述的糖果問題。每個H可以轉變爲低位的H,即一堆糖果可以取走一部分)。相同位的H可以相互抵消(即兩堆一樣的糖果,其XOR爲0,可以直接抵消,無視)。


因此本題目就與糖果問題一一對應了。一下是代碼:


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>

using namespace std;

class Solution {
public:
    void solve() {
        int n, result = 0;
        cin >> n;
        getchar();
        for (int i = 0; i < n; i++) {
            char c = getchar();
            if (c == 'H') {
                result ^= (i+1);
            }
        }
        if (result == 0) printf("Bob\n");
        else printf("Alice\n");
    }
};  

int main() {
    Solution solution;
    solution.solve();
    return 0;
} 









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章