洛谷 P2197 【模板】Nim 游戏
(这是一道代码实现十分简单的题目,就不废话,直接干吧!)
知识铺垫:
1. Nim 游戏基本概念
Nim 游戏是一种经典的组合博弈游戏。在 Nim 游戏中,有若干堆物品,两名玩家轮流操作,每次可以从任意一堆中取走任意数量的物品(不能不取),最后没有物品可取的玩家输掉游戏。
2. 异或运算异或运算
异或运算异或运算(用符号 表示)是一种二进制位运算。对于两个二进制数,相同位上数字相同则结果为 \(0\),不同则结果为 \(1\)。例如:
- 的二进制表示为 , 的二进制表示为 ,则 (二进制),转化为十进制是 。
异或运算具有以下性质: - 交换律:\(a \oplus b=b \oplus a\)
- 结合律:\((a \oplus b) \oplus c=a \oplus (b \oplus c)\)
- 对于任意整数 \(a, a \oplus a=0\)
- 对于任意整数 ,\(a,a \oplus 0=a\)
3. Nim 和
在 Nim 游戏中,Nim 和是一个关键概念。对于\(n\)堆石子,每堆石子的数量分别为\(a_1,a_2,a_3…a_{n-1},a_n\),它们的 Nim 和定义为 \(S=a_1 \oplus a_2 \oplus a_3… \oplus a_{n-1} \oplus a_n\)。
解答思路
1.必胜策略与 Nim 和的关系
Nim 游戏有一个重要的结论:当且仅当 Nim 和 \(S \ne 0\) 时,先手有必胜策略;当\(S=0\)时,先手必败。
证明思路:
- 终态:当所有堆的石子数都为 0 时,Nim 和 \(S=0\),此时轮到的玩家没有石子可取,输掉游戏。
- 状态转移:
若当前状态的 Nim 和 \(S \ne 0\),设\(S\)的二进制表示中最高位的 1 在第 \(k\)位。那么必然存在至少一堆石子,其数量的二进制表示中第\(k\)位也是 1(因为异或运算中只有对应位上有奇数个 1 结果才为 1)。设这堆石子数量为\(a_i\) ,我们可以从这堆石子中取走一些石子,使得剩下的石子数量为\(a_i{'}=a_i \oplus S\)。这样操作后,新的 Nim 和\(S'=(a_1 \oplus … \oplus a_i{'} \oplus a_i+1 \oplus …a_n)=S'=(a_1 \oplus … \oplus a_i\oplus S \oplus a_i+1 \oplus …a_n)=S \oplus S=0\) 。
若当前状态的 Nim 和\(S=0\),无论从哪一堆取走多少石子,都会使得新的 Nim 和 \(S' \ne 0\)。因为从某一堆\(a_j\)取走一些石子后,\(a_j\)变为\(a_j{'}\),且\(a_j \ne a_j{'}\) ,那么新的 Nim 和 \(S'=S \oplus a_j \oplus a_j{'}=0 \oplus a_j \oplus a_j{'}=a_j \oplus a_j{'} \ne 0\)。
代码实现:
码风可能不如人意,请见谅。
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
using namespace std;
int t,n;
int sum=0;
int main(){ios::sync_with_stdio(0);//cin 与 cout 的的加速语句cin.tie(0),cout.tie(0);cin>>t;for(int x=1;x<=t;x++){int y;sum=0;cin>>n;for(int i=1;i<=n;i++) {cin>>y;sum^=y;//逐堆异或计算总和}cout<<(sum?"Yes\n":"No\n");/*orif(sum){cout<<"Yes\n";}else{cout<<"No\n";}*/}return 0;
}