尼姆博弈

尼姆博弈(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

原文地址:https://www.cnblogs.com/AIchangetheworld/p/12183056.html