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两堆石头的游戏的主要内容,如果未能解决你的问题,请参考以下文章

力扣——Nim游戏

292. Nim 游戏

LeetCode--292--Nim游戏

算法38---292. Nim游戏

leecode练习--292Nim游戏

LeetCode292-Nim游戏(智力题)