首页 > 代码库 > nim(3)两堆石头的游戏

nim(3)两堆石头的游戏

在前面两个题目中,我们讨论了被称为"NIM(拈)“的这种游戏及其变种玩法和必胜策略,下面我们将讨论这类游戏的另一种有趣玩法。假设有两堆石头,有两个玩家会根据如下的规则轮流取石头。每人每次可以从两堆石头中各取数量相等的石头,或者仅从一堆石头中取出任意数量的石头;最后把剩下的石头一次拿光的人获胜。定义一个函数如下: bool nim(n,m) //n,m两堆石头的数量。要求返回一个布尔值,表明首先取石头的玩家是否能赢得这个游戏

解法一:和朴素的素数筛选法一样,从最小数量的石头堆开始逐个排查,比如(1,2)这个石头堆就是不安全局面,所以这个数字组合就相当于排查出一个质数来,一次以此类推。自底向上的直到排查出所有不安全局面为止。

自己写的代码如下:(时间复杂度为O(n3))

#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(3)两堆石头的游戏