P1290 欧几里德的游戏
题意:
给定两个正整数 M 和 N,从 Stan 开始,从其中较大的一个数,减去较小的数的正整数倍,当然,得到的数不能小于 0。然后是 Ollie进行同样的操作,直到一个人得到0,他就取得胜利
题解:
我们假设当前状态为(x,y),y>=x
如果y是x的倍数,先手获胜。
然后我们考虑其他情况:
假设y = kx +b,b<x
如果k>=2.说明当前状态可以转移到(x,y-(k-1)x)即(x,x+b),也就可以转移到(x,y-kx)即(x,b),而(x,x+b)也可以转移到(x,b),可得到图:
根据判定引理:(x,y)都为必胜
解释:如果(x,y)为必败,(x,x+b)为必胜,(x,y)为必胜
如果(x,y)为必胜,(x,x+b)为必败,(x,y)为必胜
如果k=1时,(x,y)=(x,x+b)---->(x,b),此时问题就变成了初始局面为(x,b),能否赢(注意此时原先的先手变成后手,所以递推时要转变状态)
代码:
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\n",a,b);
typedef long long ll;
using namespace std;inline int read(){int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
bool check(int x,int y,int p){if(x%y==0)return p;//返回当前胜者 if(y>=x*2)return p;else return check(y-x,x,p^1);
}
int main()
{int q;cin>>q;for(int i=1;i<=q;i++){int n,m;cin>>n>>m;if(n<m)swap(m,n);//保证n大,m小 //我们认为返回0先手胜,所以一开始传的也是0 if(check(m,n,0)==0)cout<<"Stan wins"<<endl;else cout<<"Ollie wins"<<endl;} return 0;
}
从Sg函数角度考虑
参考文章
最终情况为:一个数是x,另一个是0,此时先手必败
假设:现在n和m,n>m>0
sg(n,m)=mex{sg(n-m,m),sg(n-2m,m),…sg(m,n%m)}(此时n%m<m,说明交换了顺序,默认前者大于后者)
如何求sg()呢?
SG(n-m, m)=mex{SG(n-2m, m), SG(n-3m, m)…SG(m, n%m)}
sg(n-2m,m)也是同理
也就是说除了sg(m,n%m),此外所有的sg都可以由sg(m,n%m)得到
假设sg(m,n%m)==0,设n/m=k,k>=1,SG(n-(k-1)*m,m) == mex{SG(m, n%m)}=1,从此往上一直到SG(n, m)的值为2,3,4,5…,即一直必胜
如果sg(m,n%m) == 1,那么 SG(n-(k-1)*m,m) == mex{SG(m, n%m)}=0,剩下的依旧为1,2,3,4,5,6…
所以当n/m == 1时(即n=m+b,b<m),sg(n,m)!=sg(m,n%m),不然n和m就是1
其实和我一开始推是一样的,不过用sg函数更为正宗
#include <cstdio>
#define min(a, b) (a<b? a : b)
#define max(a, b) (a<b? b : a)
int T, m, n;
bool solve(int n, int m)
{if (!m)return false;if (n/m == 1)return !solve(m, n%m);else return true;
}
int main()
{scanf("%d", &T);for (int xx = 1; xx <= T; xx++){scanf("%d%d", &n, &m);if (solve(max(n, m), min(n, m)))printf("Stan wins\n");elseprintf("Ollie wins\n");}
}