hdu3595 GG and MM(博弈/辗转相除+Every-SG)

题目

有n组石子,每组石子有两堆石子,

两人轮流对每个没有结束的游戏进行操作,

每次可以从多的那一堆里减去少的的若干倍

(当然不能超过多的这一堆的上界)

两堆里至少有一堆为0就视为该子游戏结束

给定局面,问谁必胜,

胜当且仅当最后结束的一局胜,

即对手此时无法操作

思路来源

https://www.cnblogs.com/cjyyb/p/9495063.html

https://blog.csdn.net/Code92007/article/details/87892307

题解

可以说是poj2348 Euclid's Game和Every-SG的合体了

不妨一堆为a,一堆为b,且设a>=b

(1)若a大于等于两倍的b,

则先手可以把局面由(a,b)①转移到(b,a%b)②或(b+(a%b),b)③

而又由③只能转移到②,说明②③的胜负是相反的,即②③必有一先手胜必有一先手败

①只需转到先手败那个状态即可实现先手胜,故先手必胜,

特别地,若sg[b][a%b]==0,则转移到(b,a%b)

而sg[b][a%b]==1,则转移到(b+(a%b),b),再由对手一步转移到(b,a%b)

故step[a][b]=1+sg[b][a%b]+step[b][a%b]

(2)若a在b和2*b之间,显然(a,b)胜负与(b,a%b)相反,且只有这一种转移方式

故step[a][b]=step[b][a%b]+1

Every-SG游戏

即多个没结束的游戏一起玩,每个没结束的游戏都必须决策,

且必胜的局面希望玩久一点,必败的局面希望玩快一点

若v的后继为u,则

①step[v]=0,v为终止状态

②step[v]=max(step[u])+1,v为必胜态,u为v的后继,会挑步数最多的后继转移

③step[v]=min(step[u])+1,v为必败态,u为v的后继,会挑步数最少的后继转移

Every-SG定理

我们认为,最后一个结束的游戏中胜者是最终的获胜者,

那么,先手必胜,当且仅当该单一游戏的最大step为奇数

口胡证明以下三条:

①先手必胜step为奇数,后手必胜step为偶数,

数学归纳法显然,一步是先手必胜,两步是后手必胜,剩下的都是先后手轮流走

②设最大的step为max,既然max存在,说明后手只能一次令max最多减一步,

而先手一次也不得不令max减一步,说明先手可以达到这个max

③设最大的step为max,则其它必败游戏中最大step2<=max,

而这在后手必胜的情况下,先手只能一次令step2最多减一步,后手不得不令step2减一步,

同理说明后手也可达(step2-1),又因为step2<=max,故先手最多在max步结束其必败游戏

心得

算是做了一个Every-SG的例题叭

最大步长为奇数先手胜

也是更深入理解SG了

也学会了一些~i即i!=-1的技巧性写法等等

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3+5;
int n,sg[maxn][maxn],step[maxn][maxn];
int getsg(int a,int b)//a>=b
{
	if(~sg[a][b])return sg[a][b];
	if(!b)return sg[a][b]=0;
	int k=a/b,r=a%b;//a=kb+r
	if(k==1)
	{
		sg[a][b]=getsg(b,a%b)^1;
		step[a][b]=step[b][a%b]+1;
		return sg[a][b];
	}
	else
	{
		step[a][b]=getsg(b,a%b)+step[b][a%b]+1;//getsg一定放在前,不然不知道step[b][a%b] 
		return sg[a][b]=1;
	} 
}
int main()
{
	memset(sg,-1,sizeof(sg));
	while(~scanf("%d",&n))
	{
		int a,b,mx=0;
		while(n--)
		{
		 scanf("%d%d",&a,&b);
		 if(a<b)swap(a,b);
		 getsg(a,b);
		 mx=max(step[a][b],mx);
	    }
	    puts(mx&1?"MM":"GG");
	}
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值