Nim博弈
题目
有n堆物品,两人轮流取,每次取某堆中不少于1个,先取完者胜。
分析
经典问题,该问题的策略也成为了许多问题的基础。
要判断游戏的胜负只需要异或运算就可以了,有以下结论:
- $a_1 \ xor \ a_2\ xor ... \ xor a_n \neq 0$,必胜态
- $a_1 \ xor \ a_2\ xor ... \ xor a_n = 0$,必败态
为什么是异或运算呢?
//下面这段话为口胡
异或运算能保证必败态只能转移到必胜态,也就是说,当异或和为0时,从某一堆中任取至少一颗石子,异或和就一定会变成非0;
另一方面,异或运算能保证从必胜态一定可转移到必败态,也就是说,当异或和不为0时,可从某一堆中选取合适的石子(至少一个),使得异或和变成0。
应用
问题:一个排成线的格子上放有 $n$ 个棋子,棋子 $i$ 放在左数第 $p_i$ 个格子上。两人轮流选择一个棋子向左移动,每次至少移动一格,但是不允许反超其他的格子,也不允许将两个棋子放在同一个格子内。无法进行操作的一方失败。若两人都采取最优策略,谁会赢?
分析:如果将棋子两两成对当作整体考虑,我们就可以把这个游戏转成Nim游戏。
首先,考虑棋子数为偶数,我们可以两两成对,石子堆中石子的个数就等于两个石子中的间隔。如果间隔们的异或和为0,则先手移动右边的石子,后手根据Nim的策略一定能通过移动右边的石子保持异或和为0;同理,如果异或和不为0,跟Nim异或和不为0一样。除此之外,可发现,移动左边的石子是没有意义的,因为对手会跟着移。
奇数时补充一个形成偶数个。
#include<cstdio> #include<algorithm> using namespace std; const int maxn = 1000 + 10; int n, a[maxn]; int main() { int T; scanf("%d", &T); while(T--) { scanf("%d", &n); for(int i = 0;i < n;i++) scanf("%d", &a[i]); if(n&1) a[n++] = 0; sort(a, a+n); int res = 0; for(int i = 0;i < n-1;i+=2) res ^= (a[i+1] - a[i] - 1); if(res == 0) printf("Bob will win\n"); else printf("Georgia will win\n"); } }
原文地址:https://www.cnblogs.com/lfri/p/11625372.html
时间: 2024-11-03 12:20:48