Nim游戏——简单博弈论

原题链接
给定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的题吧 惊奇队长和灭霸的决战 来自临沂大学新生赛
蓝桥杯历年试题 高僧斗法 也是如此
最后放一篇写的很好的高僧斗法的题解

不当之处,请多指教。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值