nim两堆石头的游戏
Posted Flammable_ice
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nim两堆石头的游戏相关的知识,希望对你有一定的参考价值。
在前面两个题目中,我们讨论了被称为"NIM(拈)“的这种游戏及其变种玩法和必胜策略,下面我们将讨论这类游戏的另一种有趣玩法。假设有两堆石头,有两个玩家会根据如下的规则轮流取石头。每人每次可以从两堆石头中各取数量相等的石头,或者仅从一堆石头中取出任意数量的石头;最后把剩下的石头一次拿光的人获胜。定义一个函数如下: bool nim(n,m) //n,m两堆石头的数量。要求返回一个布尔值,表明首先取石头的玩家是否能赢得这个游戏。
解法一:和朴素的素数筛选法一样,从最小数量的石头堆开始逐个排查,比如(1,2)这个石头堆就是不安全局面,所以这个数字组合就相当于排查出一个质数来,一次以此类推。自底向上的直到排查出所有不安全局面为止。
自己写的代码如下:(时间复杂度为O(n³))
#include <iostream>
using namespace std;
#define M 91//分别输入两堆石头的数量
#define N 56//分别输入两堆石头的数量
const int n=M>N?M:N;
struct Stone
int a;
int b;
int flag;
s[n][n];
//两堆石头的游戏
bool NIM(int ,int )
int i,j,t=n,i1=1,j2=2;
while(t--)
int FLAG=0;
for (i=0;i<n;i++)
for (j=i;j<n;j++)
if (s[i][j].a==s[i][j].b)
s[i][j].flag=1;//标志为1的项都是先手必胜的项
if (s[i][j].a==i1||s[i][j].a==j2||s[i][j].a-i1==s[i][j].b-j2)
if (s[i][j].a<=i1&&s[i][j].b<=j2)
continue;
s[i][j].flag=1;
for (i=0;i<n;i++)
for (j=i+1;j<n;j++)
if (s[i][j].flag==0&&s[i][j].a>i1&&s[i][j].b>j2)
i1=s[i][j].a;
j2=s[i][j].b;
cout<<"("<<i1<<","<<j2<<") ";
FLAG=1;
if ((s[i][j].a==M&&s[i][j].b==N)||(s[i][j].a==N&&s[i][j].b==M))
return false;
break;
if (FLAG==1)
break;
return true;
void main()
int i,j;
for (i=n-1;i>=0;i--)
for (j=i;j<n;j++)
s[i][j].a=i+1;
s[i][j].b=j+1;
if(NIM(M,N))
cout<<"先手必赢"<<endl;
else
cout<<"先手必输"<<endl;
解法二:看到书上所给的一个公式,利用这个公式就可以求出所有不安全局面,但是对于这个公式的数学证明没看懂,我想真正面试的时候,我也不可能想到这种方法。
根据这个公式写得代码如下:(时间复杂度为O(1))
#include <iostream>
#include <math.h>
using namespace std;
bool nim(int x,int y)//whether win the game
double a,b;
a=(1+sqrt(5.0))/2;
b=(3+sqrt(5.0))/2;
if (x==y)
return true;
if (x>y)
swap(x,y);//ensure x<=y
if (x==(int)((y-x)*a)&&y==(int)((y-x)*b))
return false;
else return true;
void nim(int num)//print all the unsafe points
double a,b;
a=(1+sqrt(5.0))/2;
b=(3+sqrt(5.0))/2;
for (int i=0;i<=num;i++)
cout<<"("<<(int)(a*i)<<","<<(int)(b*i)<<") ";
cout<<endl;
void main()
cout<<nim(6,10)<<endl;
nim(100);
以上是关于nim两堆石头的游戏的主要内容,如果未能解决你的问题,请参考以下文章