第1章 游戏之乐——NIM(3)两堆石头的游戏
NIM(3)两堆石头的游戏
1. 问题描述
假设有两堆石头,有两个玩家会根据如下的规则轮流取石头:每人每次可以从两堆石头中各取出数量相等的石头,或者仅从一堆石头中取出任意数量的石头;最后把剩下的石头一次拿光的人获胜。请问在哪些局面(依据两堆石头中的石头个数)下,先取石头的玩家有必胜的策略。
2. 解法
类似构造质数的筛选方法,这里我们利用找到的必输局面(后取的玩家有必胜策略)来筛去掉能通过一次操作达该必输局面的其它必胜局面(先取的玩家有必胜策略)。最后选出的局面都是必输局面。
构造必胜策略:
如果一开始的局面就是必输局面,那么可能先取的玩家没有必胜策略(当然如果后取的玩家不太聪明,先取的玩家依然有可能能赢)。如果一开始的局面不是必输局面,那么先取的玩家一定有必胜策略,且必胜策略就是保证每次都将当前非必输局面转变为必输局面(后取的玩家必输)。
先取者有必胜策略的局面为“安全局面”,而先取者无必胜策略的局面为“不安全局面”。通过筛选可得如下不安全局面表:
不安全局面表
N | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... |
an | 1 | 3 | 4 | 6 | 8 | 9 | 11 | 12 | 14 | 16 | ... |
bn | 2 | 5 | 7 | 10 | 13 |
15 |
18 | 20 | 23 | 26 | ... |
有了通项公式,我们就能更加简明地实现函数bool nim(n,m),这个函数的时间复杂度为O(min(m,n))。代码如下:
package chapter1youxizhileNIM3;
/**
* 两堆石头的游戏
* @author DELL
*
*/
public class NIM3 {
/**
* 计算玩家是否能赢得游戏
* @param n 其中一堆石头的数量
* @param m 另一堆石头的数量
* @return 输赢状态
*/
public static boolean nim(int n, int m){
double a,b;
int temp;
a = (1+Math.sqrt(5.0))/2;
b = (3+Math.sqrt(5.0))/2;
if(m == n)
return true;
if(m < n){ //保证m>n
temp = m;
m = n;
n = temp;
}
int i = n;
while(i>=1){ //由于序号i值一定小于等于min(m,n)所以遍历查找
if((int)Math.floor(a*i)==n&&(int)Math.floor(b*i)==m)
return false;
i--;
}
return true;
}
public static void main(String[] args) {
if(nim(5,3))
System.out.println("(5,3)有必胜策略!");
else
System.out.println("(5,3)没有必胜策略!"); if(nim(3,6))
System.out.println("(3,6)有必胜策略!");
else
System.out.println("(3,6)没有必胜策略!");
} }
程序运行结果如下:
(5,3)没有必胜策略!
(3,6)有必胜策略!
3. 扩展问题
1. 现在我们已经给出了一个判断先取者是否能够最终赢得游戏的判断函数,但是,游戏的乐趣在于过程,大家能不能根据本题的思路给出一个赢得游戏的必胜策略呢?即根据当前石头的个数,给出当前玩家下一步要怎么取石头才能必胜。
程序如下:
package chapter1youxizhileNIM3;
/**
* 两堆石头的游戏
* @author DELL
*
*/
public class NIM3 {
/**
* 计算玩家是否能赢得游戏
* @param n 其中一堆石头的数量
* @param m 另一堆石头的数量
* @return 输赢状态
*/
public static boolean nim(int n, int m){
double a,b;
int temp;
a = (1+Math.sqrt(5.0))/2;
b = (3+Math.sqrt(5.0))/2;
if(m == n)
return true;
if(m < n){ //保证m>n
temp = m;
m = n;
n = temp;
}
int i = n;
while(i>=1){ //由于序号i值一定小于等于min(m,n)所以遍历查找
if((int)Math.floor(a*i)==n&&(int)Math.floor(b*i)==m)
return false;
i--;
}
return true;
} public static void result(int n,int m){
int min,max; //存储m,n中的最小值和最大值
int i;
if(m<=n){
min = m;
max = n;
}else{
min = n;
max = m;
}
if(m==n)
System.out.println("("+n+", "+m+")取后的状态为(0, 0)");
if(nim(n,m)){
for(i=1;i<=min;i++){
if(!nim(n-i,m-i)){
System.out.println("("+n+", "+m+")取后的状态为("+(n-i)+", "+(m-i)+")");
return;
}
if(!nim(n-i,m)){
System.out.println("("+n+", "+m+")取后的状态为("+(n-i)+", "+m+")");
return;
}
if(!nim(n,m-i)){
System.out.println("("+n+", "+m+")取后的状态为("+n+", "+(m-i)+")");
return;
}
}
for(i=min+1;i<=max;i++){
if(n>=m){
if(!nim(n-i,m)){
System.out.println("("+n+", "+m+")取后的状态为("+(n-i)+", "+m+")");
return;
}
}else{
if(!nim(n,m-i)){
System.out.println("("+n+", "+m+")取后的状态为("+n+", "+(m-i)+")");
return;
}
}
}
}else{
System.out.println("无论怎么取都没有必胜的策略!");
}
}
public static void main(String[] args) {
result(3,6);
result(6,6);
} }
程序运行结果如下:
(3, 6)取后的状态为(3, 5)
(6, 6)取后的状态为(0, 0)
2. 取石头的游戏已经不少了,但是我们还有一种游戏要请大家思考,我们姑且叫它NIM(4):
两个玩家,只有一堆石头,两人依次拿石头,最后拿光者为赢家。取石头的规则是:
(a)第一个玩家不能拿光所有的石头。
(b) 第一次拿石头之后,每人每次最多只能拿掉对方前一次所拿石头的两倍。
那么,这个游戏有没有必胜的算法?(提示:好像和Fibonacci数列有关。)
经分析可知,当这一堆石头的数量为Fibonacci值的时候,先拿者没有必胜策略。
判断的算法如下:
package chapter1youxizhileNIM3;
/**
* 扩展问题
* 两个玩家,只有一堆石头,两人依次拿石头,最后拿光者为赢家。取石头的规则是: (a)第一个玩家不能拿光所有的石头。 (b) 第一次拿石头之后,每人每次最多只能拿掉对方前一次所拿石头的两倍。 那么,这个游戏有没有必胜的算法?(提示:好像和Fibonacci数列有关。)
* @author DELL
*
*/
public class NIM4 {
/**
* 计算玩家是否能赢得游戏
* @param n 给定的一堆石头的数量
* @return 输赢状态
*/
public static boolean nim(int n){
int f1 = 1, f2 = 1; //Fibonacci数列的前两个值
int f3;
if(n==1)
return false;
do{
f3 = f1+f2;
f1 = f2;
f2 = f3;
}while(f3<n);
if(f3==n)
return false;
else
return true;
} public static void main(String[] args){
if(nim(3))
System.out.println("(3)有必胜策略!");
else
System.out.println("(3)没有必胜策略!");
if(nim(4))
System.out.println("(4)有必胜策略!");
else
System.out.println("(4)没有必胜策略!");
}
}
程序运行结果如下:
(3)没有必胜策略!
(4)有必胜策略!
第1章 游戏之乐——NIM(3)两堆石头的游戏的更多相关文章
- 第1章 游戏之乐——NIM(2)“拈”游戏分析
NIM(2)“拈”游戏分析 1. 问题 有N块石头和两个玩家A和B,玩家A先将石头分成若干堆,然后按照BABA……的顺序不断轮流取石头,能将剩下的石头一次取光的玩家获胜.每次取石头时,每个玩家只能从若 ...
- 第1章 游戏之乐——NIM(1)一排石子的游戏
NIM(1)一排石子的游戏 转载:编程之美-MIN(1)一排石头的游戏 1. 原题 1.1 题目 N块石头排成一行,每块石头有各自固定的位置.两个玩家依次取石头,每个玩家每次可以取其中任意一块石头,或 ...
- 编程之美 set 18 拈两堆石子游戏(3)
题目 假设有两堆石头, 有两个玩家按照如下规则轮流取石头 每个人每次可以从两堆石头中取出数量相等的石头, 或者仅从一堆石头中取出任意数量的石头 最后把剩下的石头依次拿光的人取胜 首先取石头的人能否赢得 ...
- Poj 1067 取石子游戏(NIM,威佐夫博奕)
一.Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子. ...
- 【转】Unity3D研究院之两种方式播放游戏视频
http://www.xuanyusong.com/archives/1019 Unity3D中播放游戏视频的方式有两种,第一种是在游戏对象中播放,就好比在游戏世界中创建一个Plane面对象,摄像 ...
- NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结
NIM游戏,NIM游戏变形,威佐夫博弈以及巴什博奕总结 经典NIM游戏: 一共有N堆石子,编号1..n,第i堆中有个a[i]个石子. 每一次操作Alice和Bob可以从任意一堆石子中取出任意数量的石子 ...
- (linux shell)第二章--命令之乐(一)
文章来自于我的个人博客:(linux shell)第二章--命令之乐(一) 上一章我们描写叙述了一些linux shell中须要注意的一些语法.接下来我们開始了解linux shell的经常使用 ...
- Python编写两个数的加减法游戏
目标: 1.实现两个数的加减法 2.回答者3次输错计算结果后,输出正确结果,并询问回答者是否继续 1.使用常规函数实现两个数的加减法游戏 代码如下: #!/usr/bin/env python # - ...
- NIM(1) 一排石头的游戏
最近在实习面试过程中,一个朋友遇到了该问题,从简单到复杂的思路如下,希望能给遇到相同问题的朋友一些启发和帮助.(内容来源网络和<编程之美>) 1.问题1 100个苹果 桌上有100个苹果, ...
随机推荐
- XTUOJ1250 Super Fast Fourier Transform 暴力
分析:因为加起来不超过1e6,所以最多有1000+个不同的数 做法:离散化搞就好了 #include <cstdio> #include <iostream> #include ...
- XTUOJ 1252 Defense Tower 贪心
题目链接:http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1252 思路:考虑每条边对玩家的伤害 假设连接的节点是u,v,破坏 ...
- <转>Python学习推荐
书籍推荐 基本了解: <<A Byte of Python>> (Python简明教程http://sebug.net/paper/python/) 网上有资源,两小时了解基本 ...
- vc编译器 msvcr.dll、msvcp.dll的含义和相关错误的处理
转自:http://blog.csdn.net/sptoor/article/details/6203376 很久没有写程式设计入门知识的相关文章了,这篇文章要来谈谈程式库 (Library) 连结, ...
- IOS-day03_OC中的get和set
OC中的get和set实质和C#/java中的一样 只是表现形式不同而已 如下: @interface Car : NSObject { int wheels; } -(void) run; -(vo ...
- MFC DLL 资源模块句柄切换[转]
以前写MFC的DLL的时候,总会在自动生成的代码框架里看到提示,需要在每一个输出的函数开始添加上 AFX_MANAGE_STATE(AfxGetStaticModuleState()).一直不明白这样 ...
- mysql_connect v/s mysql_pconnect
原文:mysql_connect v/s mysql_pconnect 译文:mysql_connect v/s mysql_pconnect 译者:dwqs 当需要使用PHP连接MySQL数据库的时 ...
- 1.3……Eclipse下开发Android相关知识
Android应用目录结构 src -------------------------------------------java原代码存放目录 gen ----------------------- ...
- 《Java数据结构与算法》笔记-CH2有序数组
/** * 上个例子是无序数组,并且没有考虑重复元素的情况. * 下面来设计一个有序数组,我们设定不允许重复,这样提高查找的速度,但是降低了插入操作的速度. * 1.线性查找 * 2.二分查找 * 有 ...
- redis的string类型
string : string类型是二进制安全的, 可以包含任何数据,比如jpg图片或者序列化的对象 . 方法 : set : 设置key对应的值为string类型的value set name ...