编程之美读书笔记-nim游戏

来源:互联网 发布:yy官方软件下载 编辑:程序博客网 时间:2024/06/09 18:21

题目1:N块石头排成一行,每块石头有各自固定的位置。两个玩家依次取石头,每个玩家每次可以取其中任意一块石头或相邻的两块石头,石头在游戏过程中不能移位(即编号不会改变),最后能将剩下的石头一次取光的玩家获胜。这个游戏有必胜策略吗?
解析:N=1,2时先取者可一次取完所有石头而获胜。N=3时先取者取走中间的一块石头,后取者只能取走左边或右边的石头,必将剩下一块石头。N=4时先取者取走中间的两块石头,后取者只能取走左边或右边的石头,必将剩下一块石头。至此我们发现了一个对称的规律,先取者取中间的一个(N为奇数)或两个(N为偶数)石头,确保左右两边的石头数目是一样的,之后先取者只要每次以初始中心为对称轴,在与后取者所取石头位置对称的地方取得数目相同的石头,就可以保证每次都有石头取,并且必将取得最后的石头,赢得游戏。
题目2:有N块石头和两个玩家A和B,玩家A先将石头随机分成若干堆,然后按照BABA...的顺序不断轮流取石头,能将剩下的石头一次取光的玩家获胜。每次取石头时,每个玩家只能从若干堆石头中任选一堆,取这一堆石头中任意数目(大于0)个石头。请问玩家A要怎样分配和取石头才能保证自己有把握取胜?
解析:
如果石头的个数N为偶数,A必胜。
初始:XOR(M1,M1)==0(玩家A把石头分成数目相同的两堆)
玩家B:XOR(M1,M2)!=0(玩家B将其中一堆的个数减少到M2)
玩家A:XOR(M2,M2)==0(玩家A将另一堆的个数也减少到M2)
……
结果:XOR(0,0)==0
如果石头的个数N为奇数,B必胜。
初始:XOR(M1,M2,...,Mn)!=0(玩家A把石头分成若干堆,XOR的值总是不等于0,因为必然会有奇数堆有奇数个石头)
玩家B:XOR(M1,...,Mi',...,Mn)==0(结论一:当XOR不等于0时只需改变一个值就可以让XOR等于0)
玩家A:XOR(M1,...,Mi',...,Mn)!=0(结论二:当XOR等于0时改变任意一个值都可以让XOR不等于0)
……
结果:XOR(0,...,0,...,0)==0 
证明一下结论一:假设XOR的结果k=1xxxx,xxxx可能是1或者0。那么一定存在一个Mi=xxxx1xxxx,它在k的最高位的值是1。只需要把Mi改成xxxx0xxxx的形式,0后面的位数根据结果调整使得最终结果为0。并且,改后的Mi一定比原来的Mi小。
证明一下结论二:假设XOR的结果k=0。对于任意一个Mi,如果减小了它的值一定会使得它的某一位和原来不相同,那么XOR的结果在这一位就会变成1。
题目3:假设有两堆石头,有两个玩家会根据如下的规则轮流取石头:每人每次可以从两堆石头中各取出数量相等的石头,或者仅从一堆石头中取出任意数量的石头,最后把剩下的石头一次拿光的人获胜。定义一个函数bool nim(n,m),n,m分别是两堆石头的数量,要求返回一个布尔值,表明首先取石头的玩家是否能赢得这个游戏。
解析:表中显示了在(10,10)范围内两堆石头可能的组合,由于它具有对称性,所以我们不用处理另一半的表格。另外像(0,0),(1,0)这样的特殊情况已经处理了,所以先把它们筛掉。


首先定义先取者有必胜策略的局面为安全局面,否则为不安全局面。我们把(1,1),(2,2),…(10,10)的安全局面筛掉。


这个表里面最前面的一个组合就是(1,2),通过简单的分析,我们知道这是一个不安全局面,那么根据规则可以一步到达(1,2)或者(2,1)这个局面的数字组合都是安全局面,我们可以把这些组合筛掉。


一直这样进行下去,得到下表。


找规律,发现第n组的不安全局面(an,bn)可以由以下定义得到:
1.a1=1,b1=2;
2.若a1,b1,…an-1,bn-1已经求得,则定义an为未出现在这2n-2个数中的最小整数;
3.bn=an+n。


可以根据上述定义,从第一个不安全局面出发依次向上推理,直到推理出足够的不安全局面来判定一个随机给定的状态下先取者是否能够获胜。这种解法看上去虽然直观,但是效率并不高,因为它是一种自底向上推理的算法,时间复杂度为O(N)。找出不安全局面的规律,用一个通用的公式可以表示,这样时间复杂度为O(1)。
an=[a*n],bn=[b*n]([]表示向下取整)
a=(1+sqrt(5))/2
b=(3+sqrt(5))/2

#include <cmath>#include <vector>#include <iostream>using namespace std;bool nim1(int x, int y){if (x == y) return true;if (x > y) swap(x, y);if (x == 1 && y == 2) return false;vector<int> array_bn;array_bn.push_back(2);int an = 1;int bn = 2;int delta = 1;while (x > an){while (find(array_bn.begin(), array_bn.end(), ++an) != array_bn.end());++delta;bn = an + delta;array_bn.push_back(bn);}if (x == an && y == bn) return false;return true;}void test1(int x, int y){if (nim1(x, y)) cout << "(" << x << ", " << y << ") Can Win" << endl;else cout << "(" << x << ", " << y << ") Will lose" << endl;}bool nim2(int x, int y){if (x == y) return true;if (x > y) swap(x, y);double a = (1 + sqrt(5.0)) / 2;return x != (int)floor((y - x) * a);}void test2(int x, int y){if (nim2(x, y)) cout << "(" << x << ", " << y << ") Can Win" << endl;else cout << "(" << x << ", " << y << ") Will lose" << endl;}int main() {test1(1, 2);test1(3, 5);test1(3, 6);test1(8, 10);test1(8, 13);test1(50, 50);test1(5000, 5555);test2(1, 2);test2(3, 5);test2(3, 6);test2(8, 10);test2(8, 13);test2(50, 50);test2(5000, 5555);return 0;}


0 0
原创粉丝点击