博弈论——Nim游戏

题目描述

洛谷P2197 【模板】Nim 游戏

一些观察

这是个非常经典的博弈论中的公平组合游戏,即对于A、B双方而言,二者的行动限制是一样的。

博弈论的关键在于必胜态和必败态的转化,那必胜态和必败态之间又有什么关系呢?

必胜态与必输态之间的转化

首先需要明确,先手和后手可以通过一次操作相互转换,且当前行动的人具有主动优先权,可以选择从目前的状态转移到下一个状态,而这下一个状态,即后继状态往往是非常多的。那怎么判断现在的状态是必胜态还是必败态呢?

这个时候,主动优先权就显得格外重要了——如果后继状态中存在一个必输态,那当前行动者必然会选择那个必输态,从而让另一者必输。

由此可见,必胜态就是后继状态中存在最少一个必输态。

那必输态又怎么判断呢?

因为主动优先权存在,所以若该状态存在至少一个非必胜态,则当前行动人一定会选择这个非必胜态来阻止另一者获胜,故而必输态应是后继状态中全是必胜态。

具体题目分析

在本题中,若是从必输态一个个往前推,会发现时间复杂度和空间复杂度都会爆。那有没有什么简单的方法判断当前状态是什么状态呢?

这个时候,我们可以试着分析一下每个数都可以转化成的东西——二进制

若我们求出所有数的异或和,并观察每个数的二进制位,就会发现一个性质:

  • 在所有数的异或和的二进制中,某一位的数值为0则意味着存在偶数个数的二进制在这一位上为1;数值为1则意味着存在奇数个数的二进制在这一位上为1。

举个例子:

3:011
6:110
4:100

可以发现,这三个数的异或和为001,且在这三个数中,第三位有两个数的数值为1,第二位有两个数的数值为1,第三位有一个数的数值为1,符合上述规律。

那这个规律又有什么用呢?用处可大了!

通过观察可以发现一个显然的必然条件:当剩下的石子的总数为0时,石子数量的异或和定然为0

因为每个人都无法不取,故在初始状态下异或和不为0时,先手总是可以通过一步操作把异或和变为0。

证明:
假设有 n 个数, a1 ~ an,它们的异或和为 s ,且 s 在二进制下的最高位为第 x 位。
考虑:若要使后继状态的异或和为0,则必须存在一个 ai,使 ai 可以通过减某个正整数转换成 ai ^ s,即 ai >= ai ^ s
因为 s 在二进制下的最高位为第 x 位,故根据上述规律可以得出:在这 n 个数中,存在一个数 ai ,其在二进制下的第 x 位的值为1,则根据异或性质可得 ai ^ s < ai,即该数可以通过减去某个正整数转化成 ai ^ s。

这个时候,问题就转换成了:找到一个策略,在石子数量不为0且异或和为0时,通过两次操作,转换成石子数量减少而异或和依旧为0的状态。可以轻易证明,在这种策略下,第一个行动的人必输

而事实上,根据在初始状态下异或和不为0时,先手总是可以通过一步操作把异或和变为0的结论,我们已经得出了这个策略——第一个人拿走 x 个石子,不就相当于石子数的异或和变为 x 吗?那第二个人不就必然可以把异或和变为0,第一个人不就必然输了吗?

综上,该问题就得解了——把所有石子的数量的异或和求出来,为0则先手必输,反之先手必胜。

完整代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n,T,a[N],sum,ans;
int main(){
	cin>>T;
	while(T--){
		cin>>n;
		ans=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			ans^=a[i];
		}
		if(ans){
			printf("Yes\n");
		}
		else{
			printf("No\n");
		}
	}
	return 0;
}

推荐题目

洛谷P1247 取火柴游戏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值