题目1 : 博弈游戏·Nim游戏·二
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
- 样例输入
-
8 HHTHTTHT
- 样例输出
-
Bob
描述
Alice和Bob这一次准备玩一个关于硬币的游戏:
N枚硬币排成一列,有的正面朝上,有的背面朝上,从左到右依次编号为1..N。现在两人轮流翻硬币,每次只能将一枚正面朝上的硬币翻过来,并且可以随自己的意愿,在一枚硬币翻转后决定要不要将该硬币左边的任意一枚硬币也翻一次(正面翻到背面或背面翻到正面)。翻最后一枚正面向上的硬币的人获胜。同样的,这次游戏里面Alice仍然先手,两人均采取最优的策略,对于给定的初始局面,Alice会获胜还是Bob会获胜?
输入
第1行:1个正整数N,表示硬币数量。1≤N≤10,000
第2行:1个字符串,第i个字符表示编号为i的硬币状态,’H’表示正面朝上,’T’表示背面朝上。
输出
第1行:1个字符串,若Alice能够获胜输出"Alice",否则输出"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; }