关于尼姆堆问题的详细解释

    关于博弈论中的Nim游戏,在蓝桥杯省赛前看了一下,很惭愧,看了半天没看很明白,就放弃了(赛前背了一个模版,幸运的是没考到)今天突然看到一道相关的题目,于是回头又看了看,可能灵感突然来了,基本看明白了,下面我就用最最最浅显的语言表达方式来对Nim游戏做一个详细的解释。



    Nim游戏:有n个石堆,每堆里有数量一定的石子,两人从其中任意一堆中取任意数量的石子(不能超过这堆石子数的最大值),不能不取,最后某个人取完,所有石堆中的石子数量都为0时,另一个人就为输。



    题目大致就是这样,在解这道题目前,有几个概念需要知道的:

    1.异或运算符(^),异或是一种对于两个数的二进制数进行运算的逻辑运算符,同真同假为假,一真一假为真,即1^1=0,0^0=0,1^0=1。


    2.定义两个状态,分别为N和P,N代表Next-position,可以理解为先手必胜状态,P代表Previous-position,可以理解为后手必胜状态(或为先手必败)。


    3.N/P两个状态的关系:(1)无法进行局面转移的状态为P;

                                    (2)只要有一种转移方式能将局面变为P,则当前状态为N;

                                    (3)任何转移方式都只能使局面变为N,则当前状态为P。

    我在这里通过Nim游戏对上面三句话做一个简单的解释:

                                    (1)最后所有石堆中的石子都为0,此时无法再做任何操作,所以此时是P;

                                    (2)在还剩一堆的情况下(无论个数,因为可以随意取),把这一堆都取完,留给对方                                                           的肯定是0个石子,也就是P,所以当前肯定是N;

                                   (3)还剩下两堆,且每堆个数为1,因为当前必须要取,而当取完后,通过(2)可知,                                                          留给对方的局 面一定是N,所以当前肯定为P。

    以上作出的解释都为特殊情况,只是方便理解。




    概念就是上面几个,下面开始解Nim问题:

    首先说结论:对于Nim游戏的各个石堆(a1,a2,a3......an),当且仅当a1^a2^a3^......^an=0时它为P。

                      看到这个结论是,确实让人感到很神奇,博弈问题竟然能和异或扯上关系,下面我对这个结论进行论证。

    论证还是要根据N/P的三个状态关系来:

(1)无法移动的局面为所有石堆的石子数都为0,0^0^......^0=0,这个很简单;

(2)若当前的局面不为0,即a1^a2^a3^......^an=k,则必定有一个数ai改变ai的值为ai'(或者理解为改变当前某个石堆的状态),能使的a1^a2^a3^......^ai'^......^an=0,证明很简单,通过异或的性质,因为k的二进制最高位(从右往左)为1,可以得出一 定存在某个ai的最高位为1,此时ai'*k<ai,这个也很好理解,当前的最高位由1变为0 ,十进制时ai一定小于ai'。所以,我们可以将ai=ai'*k,此时a1^a2^a3^......^ai'*k ^.......an=0。

(3)若当前某个局面为P,即a1^a2^a3^......^an=0,一定不存在改变某 个ai,使之变为ai',a1^a2^a3^.......^ai'^......^an=0,因为异或满足消去律,消去后得ai=ai',由题目可得不能不取,即当前的ai值必须改变,所以此时ai一定不等于ai'。

    



    理论证明就是以上这些,下面给出代码

class Main {
	public static void main(String[] args) {
		int x []={2,5,12,14};
		f(x);
	}
	static void f(int x []) {
		int sum=0;
		for(int i=0;i<x.length;i++)
			sum^=x[i];
		if(sum==0)
			System.out.println("必输");
		for(int i=0;i<x.length;i++) {
			int k=sum^x[i];
			if(k<x[i])
				System.out.println("原堆个数为"+x[i]+"的子堆变为"+k);//得出的结果为所有的方案第一次移动的数
		}
	}
}

    

    结果如下




以上就是Nim问题的全部解题过程,如果有错误的欢迎指出,感谢!


  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值