【博弈论】Nim游戏

百度百科

Definition

这样的游戏被称为Nim游戏:
1、有两个玩家,轮流进行操作
2、是公平游戏。即面对同一局面两个玩家所能进行的操作是相同的。例如中国象棋不是公平游戏。因为面对同一个局面,红方只能移动红色棋子而不能移动黑方棋子,黑房同理。
3、一个玩家是输掉当且仅当他无法进行操作。例如如果是两个人轮流取石子的游戏,那么一个玩家输掉当且仅当他面前没有石子了。因为他下面无法进行取石子的操作。
一般的,Nim游戏指两个玩家轮流在好几堆东西中取物品,不能夸堆取,无法操作判负。

Solution

对于Nim游戏的解法,因为状态显然是不可逆的,所以可以对状态的转移图进行拓扑排序然后DP求解。但是对于大部分博弈题目,状态多的难以计算,所以需要考虑更简单的方法。

结论:一个Nim游戏中的状态是必败状态当且仅当每个子游戏的异或和为0.

这里子游戏代表构成Nim游戏中最基础的游戏。例如两个人轮流取三堆石子的游戏是三个取一堆石子的游戏组合而成的。对于每个子游戏显然可以用一个正整数代表当前的状态,即还剩多少石子。Nim游戏的子游戏状态异或和为0,则必败。
证明:我有一个绝妙的想法,可惜这里写不开
对于一个异或和不为0的状态,那么他是必胜态。
考虑在一个必胜态下如何进行下一步。
设异或和是X。设X二进制的最高为1位是第k位。
那么显然存在至少一个状态使得他们二进制第k位为1。否则第k位异或和应该是0。
那么就任选一个第k位是1的状态,设他的状态是Y,那么将他可以将Y这一堆取成Z=X xor Y那么得到的状态是必败状态。

证明如下:

首先,Y可以取成Z当且仅当Z  Y。首先证明Z  Y
因为YXk位都是1,更高位都是0。那么异或后Zk位一定是0,更高位显然是0。所以Z的位数比Y的位数要少。那么显然Z  Y
下面证明更改后的状态是必败状态
根据Z=X xor Y,并且A xor A= 0 。可得:
更改后的状态
S = X xor Y xor Z = X xor Y xor ( X xor Y )
= ( X xor Y ) xor ( X xor Y ) = 0 
一定是一个必败状态。

Example

传送门

Description

输入k及k个整数n1,n2,…,nk,表示有k堆火柴棒,第i堆火柴棒的根数为ni;接着便是你和计算机取火柴棒的对弈游戏。取的规则如下:每次可以从一堆中取走若干根火柴,也可以一堆全部取走,但不允许跨堆取,也不允许不取。

Input

第一行,一个正整数k
第二行,k个整数n1,n2,…,nk

Output

如果是先取必胜,请在第一行输出两个整数a,b,表示第一次从第b堆取出a个。第二行为第一次取火柴后的状态。如果有多种答案,则输出<b,a>字典序最小的答案(即b最小的前提下a最小)。
如果是先取必败,则输出“lose”。

Sample Input_1

3
3 6 9

Sample Output_1

4 3
3 6 5

Sample Input_2

4
15 22 19 10

Sample Output_2

lose

Hint

k  500000
ni  1e9

Solution

板子题要啥solution

Code

#define rg register
#define ci const int
#define cl const long long int

typedef long long int ll;

namespace IO {
	char buf[90];
}

template<typename T>
inline void qr(T &x) {
	char ch=getchar(),lst=' ';
	while(ch>'9'||ch<'0') lst=ch,ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(lst=='-') x=-x;
}

template<typename T>
inline void write(T x,const char aft,const bool pt) {
	if(x<0) x=-x,putchar('-');
	int top=0;
	do {
		IO::buf[++top]=x%10+'0';
		x/=10;
	} while(x);
	while(top) putchar(IO::buf[top--]);
	if(pt) putchar(aft);
}

template<typename T>
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template<typename T>
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template<typename T>
inline T mabs(const T a) {if(a<0) return -a;return a;}

template<typename T>
inline void mswap(T &a,T &b) {
	T temp=a;a=b;b=temp;
}

const int maxn = 500010;

int n,ans;
int MU[maxn];

int main() {
	qr(n);
	for(rg int i=1;i<=n;++i) {
		qr(MU[i]);ans^=MU[i];
	}
	if(!ans) {
		puts("lose");return 0;
	}
	int _temp=1<<30;
	while(!( _temp & ans )) _temp>>=1;
	for(rg int i=1;i<=n;++i) if(MU[i] & _temp){
		int z=ans^MU[i];
		write(MU[i]-z,' ',true);write(i,'\n',true);
		for(rg int j=1;j<=n;++j) {
			if(j != i) write(MU[j],' ',true);
			else write(z,' ',true);
		}
		break;
	}
	return 0;
}

Summary

Nim游戏是必败态当且仅当子游戏状态异或和为0。否则是必胜态。通过必胜态一定可以通过一步操作变成必败状态。

posted @   一扶苏一  阅读(5342)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示