尼姆博弈(Nimm's Game)
n堆物品,两人轮流取至少一个,最后一个取光的人胜利
1 int res = 0; 2 for(int i=1;i<=n;i++){ 3 res = res ^ a[i]; 4 } 5 if(res) return true; 6 else return false;
题目:有3堆硬币,分别是3,4,5。两人轮流取硬币,每人每次只能从某一堆中取至少一枚硬币,取到最后一枚硬币的为赢家。求先取硬币一方有无必胜的招法。
分析
首先可以想到,如果最后剩下两堆硬币一样多(不为零),最后一堆为零,那面对这一局势的人必输。
用(a,b,c)表示某种局势。(0,0,0)显然是必输态,谁遇到这种状态必输;另一种必输态就是(n,n,0),自己在某一堆拿走k枚硬币(0<k<=n),k在该区间为任意值,对方只要在另一堆中拿走k枚硬币,最后都是自己面临(0,0,0)状态,必输;仔细分析,(1,2,3)也是必输态,无论自己如何拿,对手都可以把局势变成(n,n,0)态。
因此现在要找到这种奇异局势的特点。
有人就想到使用异或操作,异或‘^’,a^b = a'b+ab'(a'为非a)。
我们使用XOR表示这种运算,1 XOR 1 = 0。(1,2,3)的按位模2加的运算:
1 = 01
2 = 10
XOR 3 = 11
----------------------------
0 = 00(不进位)
对于(n,n,0)局势也一样,结果为0。
任何奇异局势(a,b,c)都有a XOR b XOR c = 0
如果我们面对的是一个非必输态(a,b,c),如何保持自己必胜,让对手面对必输态呢?
假设a<b<c,我们只需将c变为a XOR b。
因为a XOR b XOR (a XOR b) = (a XOR a)XOR(b XOR b)= 0 XOR 0 = 0。
要将c变为a XOR b,只需对c进行|c - (a XOR b)|运算即可(取绝对值就行)。
如例题中的(3,4,5),先3 XOR 4 = 111,再|101 - 111| = 010 = 2。
1 static void f(int[] a){ 2 int sum = 0;//与0进行异或操作值不变 3 for(int i = 0; i < a.length; i++){ 4 sum ^= a[i];//异或操作 5 } 6 if(sum == 0){ 7 System.out.println("输了"); 8 return; 9 } 10 //打印必胜的方法 11 for(int i=0; i<a.length; i++){ 12 int x = sum ^ a[i]; 13 if(x<a[i]) System.out.println(a[i] + " --> " + x); 14 } 15 }
因此面对开局非必输态(a,b,c)且先手,取得必胜操作就是:对a进行|a - (b XOR c)| 或者 对b进行|b - (a XOR c)| 或者 |c - (a XOR b)|。
推荐相关文章:https://www.cnblogs.com/jiangjun/archive/2012/11/01/2749937.html