博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第1章 游戏之乐——NIM(3)两堆石头的游戏
阅读量:6907 次
发布时间:2019-06-27

本文共 5221 字,大约阅读时间需要 17 分钟。

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))。代码如下:

 

1 package chapter1youxizhileNIM3; 2 /** 3  * 两堆石头的游戏 4  * @author DELL 5  * 6  */ 7 public class NIM3 { 8     /** 9      * 计算玩家是否能赢得游戏10      * @param n 其中一堆石头的数量11      * @param m 另一堆石头的数量12      * @return 输赢状态13      */14     public static boolean nim(int n, int m){15         double a,b;16         int temp;17         a = (1+Math.sqrt(5.0))/2;18         b = (3+Math.sqrt(5.0))/2;19         if(m == n)20             return true;21         if(m < n){  //保证m>n22             temp = m;23             m = n;24             n = temp;25         }26         int i = n;27         while(i>=1){ //由于序号i值一定小于等于min(m,n)所以遍历查找28             if((int)Math.floor(a*i)==n&&(int)Math.floor(b*i)==m) 29                 return false;30             i--;31         }32             return true;33     }34     public static void main(String[] args) {35         if(nim(5,3))36             System.out.println("(5,3)有必胜策略!");37         else38             System.out.println("(5,3)没有必胜策略!");39         40         if(nim(3,6))41             System.out.println("(3,6)有必胜策略!");42         else43             System.out.println("(3,6)没有必胜策略!");44     }45 46 }

程序运行结果如下:

(5,3)没有必胜策略!(3,6)有必胜策略!

 3. 扩展问题

  1. 现在我们已经给出了一个判断先取者是否能够最终赢得游戏的判断函数,但是,游戏的乐趣在于过程,大家能不能根据本题的思路给出一个赢得游戏的必胜策略呢?即根据当前石头的个数,给出当前玩家下一步要怎么取石头才能必胜。

程序如下:

1 package chapter1youxizhileNIM3; 2 /** 3  * 两堆石头的游戏 4  * @author DELL 5  * 6  */ 7 public class NIM3 { 8     /** 9      * 计算玩家是否能赢得游戏10      * @param n 其中一堆石头的数量11      * @param m 另一堆石头的数量12      * @return 输赢状态13      */14     public static boolean nim(int n, int m){15         double a,b;16         int temp;17         a = (1+Math.sqrt(5.0))/2;18         b = (3+Math.sqrt(5.0))/2;19         if(m == n)20             return true;21         if(m < n){  //保证m>n22             temp = m;23             m = n;24             n = temp;25         }26         int i = n;27         while(i>=1){ //由于序号i值一定小于等于min(m,n)所以遍历查找28             if((int)Math.floor(a*i)==n&&(int)Math.floor(b*i)==m)29                 return false;30             i--;31         }32             return true;33     }34     35     public static void result(int n,int m){36         int min,max; //存储m,n中的最小值和最大值37         int i;38         if(m<=n){39             min = m;40             max = n;41         }else{42             min = n;43             max = m;44         }45         if(m==n)46             System.out.println("("+n+", "+m+")取后的状态为(0, 0)");47         if(nim(n,m)){48             for(i=1;i<=min;i++){49                 if(!nim(n-i,m-i)){50                     System.out.println("("+n+", "+m+")取后的状态为("+(n-i)+", "+(m-i)+")");51                     return;52                 }53                 if(!nim(n-i,m)){54                     System.out.println("("+n+", "+m+")取后的状态为("+(n-i)+", "+m+")");55                     return;56                 }57                 if(!nim(n,m-i)){58                     System.out.println("("+n+", "+m+")取后的状态为("+n+", "+(m-i)+")");59                     return;60                 }61             }62             for(i=min+1;i<=max;i++){63                 if(n>=m){64                     if(!nim(n-i,m)){65                         System.out.println("("+n+", "+m+")取后的状态为("+(n-i)+", "+m+")");66                         return;67                     }68                 }else{69                     if(!nim(n,m-i)){70                         System.out.println("("+n+", "+m+")取后的状态为("+n+", "+(m-i)+")");71                         return;72                     }73                 }74             }75         }else{76             System.out.println("无论怎么取都没有必胜的策略!");77         }78     }79     public static void main(String[] args) {80         result(3,6);81         result(6,6);82     }83 84 }

程序运行结果如下:

(3, 6)取后的状态为(3, 5)(6, 6)取后的状态为(0, 0)

   2. 取石头的游戏已经不少了,但是我们还有一种游戏要请大家思考,我们姑且叫它NIM(4):

  两个玩家,只有一堆石头,两人依次拿石头,最后拿光者为赢家。取石头的规则是:

    (a)第一个玩家不能拿光所有的石头。

    (b) 第一次拿石头之后,每人每次最多只能拿掉对方前一次所拿石头的两倍。

  那么,这个游戏有没有必胜的算法?(提示:好像和Fibonacci数列有关。)

  经分析可知,当这一堆石头的数量为Fibonacci值的时候,先拿者没有必胜策略。

判断的算法如下:

 

1 package chapter1youxizhileNIM3; 2 /** 3  * 扩展问题 4  *  两个玩家,只有一堆石头,两人依次拿石头,最后拿光者为赢家。取石头的规则是: 5  6     (a)第一个玩家不能拿光所有的石头。 7  8     (b) 第一次拿石头之后,每人每次最多只能拿掉对方前一次所拿石头的两倍。 9 10   那么,这个游戏有没有必胜的算法?(提示:好像和Fibonacci数列有关。)11  * @author DELL12  *13  */14 public class NIM4 {15     /**16      * 计算玩家是否能赢得游戏17      * @param n 给定的一堆石头的数量18      * @return 输赢状态19      */20     public static boolean nim(int n){21         int f1 = 1, f2 = 1;  //Fibonacci数列的前两个值22         int f3;23         if(n==1)24             return false;25         do{26             f3 = f1+f2;27             f1 = f2;28             f2 = f3;29         }while(f3

程序运行结果如下:

(3)没有必胜策略!(4)有必胜策略!

 

转载地址:http://qwgdl.baihongyu.com/

你可能感兴趣的文章
Django之路由系统
查看>>
KVM autotest
查看>>
Python语言特性之3:@staticmethod和@classmethod
查看>>
MBR和GPT分区学习
查看>>
1.单一职责原则(Single Responsibility Principle)
查看>>
LeetCode--058--最后一个单词的长度
查看>>
Mysql JSON 新特性用法
查看>>
python List 对象
查看>>
@RequestParam注解的使用
查看>>
JQ_简单图片拖动
查看>>
【直视骄阳】生命的留言
查看>>
shell脚本:不登陆KVM虚拟机,修改虚拟机网卡IP地址
查看>>
性能测试loadrunner场景问题之socket
查看>>
fdisk分区命令详解与fdisk非交互式分区
查看>>
LINUX下PHP运行环境搭建之三(转)
查看>>
asp.net中连接字符串问题(类库中使用ConfigurationManager.ConnectionStrings)
查看>>
艾伟_转载:Web网站缓存文件并发问题解决方案
查看>>
iOS LaunchScreen设置启动图片 启动页停留时间
查看>>
android137 360 双击三击事件
查看>>
PHP-002
查看>>