给定n堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数n。
第二行包含n个数字,其中第 i 个数字表示第 i 堆石子的数量。
输出格式
如果先手方必胜,则输出“Yes”。
否则,输出“No”。
数据范围
1≤n≤105,
1≤每堆石子数≤109
输入样例:
2
2 3
输出样例:
Yes
先上一波代码
#include<bits/stdc++.h> using namespace std; int main(){ int n; int res=0; cin>>n; while(n--){ int x; cin>>x; res^=x; } if(res) cout<<"Yes"<<endl; else cout<<"No"<<endl; return 0; }
该题为简单的 博弈论题目,一般博弈论题目有如下特征:
1.有两名选手;
2.两名选手交替操作,每次一步,每步都是在有限的合法集合中选取一种进行;
3.在任何情况下,合法操作只取决于情况本身,与选手无关;
4.游戏的败北条件为:当某位选手需要进行操作时,当前没有任何可以执行的合法操作,则该选手败北。
先来介绍Nim里的两种状态,先手必败状态和先手必胜状态。 前者是先手走不到任何一个必败状态,即后手无法是必败状态,先手必败。 后者即先手可以走到某个必败状态,此时为后手操作,后手必败。
Nim游戏中有个经典结论:若a1^a2…an==0 则先手必败。
我们可以从简单的数据入手,比如有两堆石子的数量分别为2、3。那么是不是先手必胜呢。我们可以先用结论及代码验证一下。
#include<bits/stdc++.h> using namespace std; int main(){ int a1 = 2 , a2 = 3 ; if(a1^a2==0) puts("Win"); else puts("Lose"); return 0; }
运行结果如图
那么我们现在可以模拟一下具体过程,前提是每个人每一步都是最优策略。先手从石子数为3的堆里取走一个石子,此时变为2 2,接下来不论后手做任何操作,先手只需在另一堆石子里做相同的操作,这样就再次使得两堆石子的数目一样。如此循环下去,一定是后手遇到必败状态,先手胜利。
接下来进行简单的证明一下:
首先,当所有的堆的石子数均为0时,异或值也是0,即不能进行任何操作,先手必败。
然后,如果异或值不为零,即a1^a2…an= k(k为非零常数),先手一定可以某种操作(即从某堆石子中拿走部分石子)让异或值变为0,此时后手走到了必败态,先手必胜。(具体为什么是异或值还有待探索)
假设k的二进制表示中最高的一位1是第x位, 那么在a1-an中必存在有一个数ai的第x位是1;那么ai^x<ai(ai的第k位由1变为0),我们可以从ai个石子中拿走ai-ai ^x 个石子(操作一定合法),这样的话所有的数的异或值就是0。
最后,我们来证明如果a1^a2…an=0,无论如何拿,所有数的异或值一定不是0。反证法可证。
具体为什么是异或值为0,可以参考K-Nim游戏;
Nim还有很多变形,学习中。
扔一道台阶Nim的题吧 惊奇队长和灭霸的决战 来自临沂大学新生赛
蓝桥杯历年试题 高僧斗法 也是如此
最后放一篇写的很好的高僧斗法的题解
不当之处,请多指教。