南昌大学航天杯第二届程序设计竞赛校赛网络同步赛B 取石子(博弈&SG函数模板)

题目链接:取石子


链接:https://www.nowcoder.com/acm/contest/122/B

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述 

现在有两堆石子,两个人轮流从中取石子,且每个人每一次只能取1、3或9个石子,取到最后一个石子的人win。
假设先手后手都会选择最好的方式来取石子,请您判断先后手的输赢情况。

输入描述:

多组输入
每组一行,一行包括两个正整数n1和n2(1<=n1<=100,1<=n2<=100),代表了两堆石子的数目

输出描述:

如果先手能赢,输出"win";否则就输出"lose"。
示例1

输入

复制
1 1 
1 2

输出

复制
lose
win
 

参考文章:点击打开链接

博弈论总结:点击打开链接

1、定义:

(1)只有两人参与。

(2)游戏局面的状态集合是有限。

(3)对于同一个局面,两个游戏者的可操作集合完全相同

(4)游戏者轮流进行游戏。

(5)当无法进行操作时游戏结束,此时不能进行操作的一方算输。

(6)无论游戏如何进行,总可以在有限步数之内结束。

1.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);

2.可选步数为任意步,SG(x) = x;

3.可选步数为一系列不连续的数,用GetSG()计算

模板1如下(SG打表):

[cpp]  view plain  copy
  1. //f[]:可以取走的石子个数  
  2. //sg[]:0~n的SG函数值  
  3. //hash[]:mex{}  
  4. int f[N],sg[N],hash[N];       
  5. void getSG(int n)  
  6. {  
  7.     int i,j;  
  8.     memset(sg,0,sizeof(sg));  
  9.     for(i=1;i<=n;i++)  
  10.     {  
  11.         memset(hash,0,sizeof(hash));  
  12.         for(j=1;f[j]<=i;j++)  
  13.             hash[sg[i-f[j]]]=1;  
  14.         for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数  
  15.         {  
  16.             if(hash[j]==0)  
  17.             {  
  18.                 sg[i]=j;  
  19.                 break;  
  20.             }  
  21.         }  
  22.     }  
  23. }  

模板2如下(dfs):

[cpp]  view plain  copy
  1. int f[105],sg[1005];//f[]为所要拿取的球数    
  2. int sg_dfs(int x)    
  3. {    
  4.     if(sg[x]!=-1)    
  5.         return sg[x];    
  6.     bool visited[1005];//必须在函数里面定义,在全局定义会出错    
  7.     memset(visited,0,sizeof(visited));    
  8.     for(int i=1;i<=30;i++)//i<=f[]的项数    
  9.     {    
  10.         if(x>=f[i])    
  11.             visited[sg_dfs(x-f[i])]=1;    
  12.     }    
  13.     for(int i=0;;i++)    
  14.         if(!visited[i])    
  15.         {    
  16.             sg[x]=i;    
  17.             break;    
  18.         }    
  19.     return sg[x];    
  20. }    
  21. int main()    
  22. {    
  23.         memset(sg,-1,sizeof(sg));//sg[]初始化为-1    
  24.         sort(f,f+n);//将f[]排列    
  25.         if(sg_dfs(m)^sg_dfs(n)^sg_dfs(p))    
  26.             cout<<1<<endl;    
  27.         else    
  28.             cout<<0<<endl;    
  29.     return 0;    
  30. }    

AC代码:

#include<stdio.h>
#include<string.h>

int f[5],sg[1010];//f[]为所要拿取的球数 

int sg_dfs(int x)
{
	if(sg[x]!=-1) return sg[x];
	int book[1010];//必须在函数里定义,全局定义会出错 
	memset(book,0,sizeof(book));
	for(int i=1;i<=3;i++)//i<=f[]的项数 
	{
		if(x>=f[i]) book[sg_dfs(x-f[i])]=1;
	}
	for(int i=0;;i++)
	{
		if(book[i]==0)
		{
			sg[x]=i;
			break;
		}
	}
	return sg[x];
}

int main()
{
	memset(sg,-1,sizeof(sg));//sg[]初始化为-1 
	f[1]=1;
	f[2]=3;
	f[3]=9;
	int n,m;
	while(~scanf("%d%d",&n,&m))
	{
		if(sg_dfs(n)^sg_dfs(m)==0) printf("lose\n");
		else printf("win\n");
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值