问题描述:
欧几里德的两个后代Stan和Ollie正在玩一种数字游戏,这个游戏是他们的祖先欧几里德发明的。给定两个正整数M和N,从Stan开始,从其中较大的一个数,减去较小的数的正整数倍,当然,得到的数不能小于0。然后是Ollie,对刚才得到的数,和M,N中较小的那个数,再进行同样的操作……直到一个人得到了0,他就取得了胜利。下面是他们用(25,7)两个数游戏的过程:
Start:25 7
Stan:11 7
Ollie:4 7
Stan:4 3
Ollie:1 3
Stan:1 0
Stan赢得了游戏的胜利。
现在,假设他们完美地操作,谁会取得胜利呢?
递归爆栈T_T代码:
long long m,n;
bool dfs(long long m,long long n)
{
if(m == 0 || n == 0)//边界条件
return true;
if(m > n)//分情况讨论
{
for(int i = 1; ; ++i)//从1倍开始减
{
if(m - i * n < 0)
break;
if(dfs(n,m - i * n) == true)//如果对手必胜
return false;//我方必败
}
}
else
{
for(int i = 1; ; ++i)//从1倍开始减
{
if(n - i * m < 0)
break;
if(dfs(m,n - i * m) == true)//如果对手必胜
return false;//我方必败
}
}
return true;
}
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> m >> n;
if(m % n == 0 || n % m == 0)//特判
{
puts("Stan wins");
continue;
}
if(dfs(m,n) == true)//搜索
puts("Ollie wins");
else
puts("Stan wins");
}
return 0;
}
AC代码,解法①:
int n,m;
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> m >> n;
if(m == n)
puts("Stan wins");
else
{
if((max(m,n) * 1.0) / min(m,n) > (sqrt(5) + 1) / 2)
puts("Stan wins");
else
puts("Ollie wins");
}
}
return 0;
}
AC代码,解法②:
bool solve(int n,int m)
{
if(m == 0)
return false;
if(n / m == 1)
return !solve(m,n % m);
else//n / m = 2,3,4,......
return true;
}
int main()
{
int t;
cin >> t;
while(t--)
{
int n,m;
cin >> n >> m;
if(solve(max(n,m),min(n,m)) == true)
puts("Stan wins");
else
puts("Ollie wins");
}
return 0;
}
AC代码,解法③:
int main()
{
int t;
cin >> t;
while(t--)
{
int n,m;
cin >> n >> m;
if(n < m)
swap(n,m);
if(n / m >= 2)//先手必胜
{
puts("Stan wins");
continue;
}
int who = 1;//1代表先手,为Stan
while(n / m == 1 && n % m != 0)//当循环结束时,有n / m >= 2或者n % m == 0二者情况都是先手必胜
{
//进入循环说明mm而不是n>=m是因为循环中有条件
n = n % m;//只能这么取,或者写成n-m
swap(n,m);//维护相对大小
who = -who;
}
if(who == 1)
puts("Stan wins");
else
puts("Ollie wins");
}
return 0;
}
解决方法:
爆栈那个,就不多说了,太无脑了。
解法①是个结论,不太懂。如果两个数相等,或者两数之比大于 5√+12 ,则先手胜,否则后手胜。
解法②的分析如下:
我们可以将两个数字转换为两堆石子,其大小分别为n、m。
这样就把游戏转换为了Nim游戏的变形,于是想到用SG函数的方法来求解
SG函数有两个变量n、m,表示当前的局面。
由于SG函数是通过逆推求解的,所以我们从终止局面开始考虑
不妨设 n>m
当m = 0时,先手必败。此时SG(n,m) = 0,这是SG函数定义中的边界情况
其他情况:SG(n,m) = mex{SG(n - m),SG(n - 2m),......,SG(n - (k - 1)m,m),SG(m,n % m)},k = n / m
n - km = n % m
那么mex函数内的SG值怎么求呢
SG(n - m) = mex{SG(n - 2m),SG(n - 3m),......,SG(m,n % m)}
SG(n - 2m) = mex{SG(n - 3m),SG(n - 4m),......,SG(m,n % m)}
可以看出,除了SG(m,n % m)
以外的其他SG函数值都可以由SG(m,n % m)}
得来
如果SG(m,n % m) = 0,k = n / m
,那么SG(n - (k - 1)m,m) = mex{SG(m,n % m)} = 1
其他SG值分别为2,3,4,5,……均为必胜态,可以简记为1
如果SG(m,n % m) = 1,k = n / m
,那么SG(n - (k - 1)m,m) = mex{SG(m,n % m)} = 0
其他SG值仍为2,3,4,5,……
⌊n−(k−1)mm⌋=⌊n−km+mm⌋=⌊n%m+mm⌋=1
所以对于任意局面的SG(n,m)
如果n/m = 1,则SG(n,m)=!SG(m,n % m)
,否则就是1(简化的)
解法③分析:
这个问题还可以转化为巴什博奕,因为减去的数(相当于拿走的石子)是有上限的,还是转化为取石子游戏来说吧。
不妨设 n>m
至少取走m个石子,至多取走k * m,k = n / m
个石子
不一样的地方在于取的石子数并不是连续的,而是较小的数m的整数倍,不能直接套巴什博奕的结论
但是我们仍然可以利用巴什博奕的思想,当n >= 2m时,来看状态(n%m,m)
如果(n%m,m)为P局面,则先手只需将状态(n,m)转为(n%m,m),必胜
如果(n%m,m)为N局面,则先手将状态(n,m)转移为(n % m + m,m),则后手只能将状态转移为(n%m,m),N局面回到先手的手中,先手必胜
总之,当n >= 2m时,先手必胜
否则,只能从n中取走m,然后利用上述结论进行循环或者递归。
还要判断n%m==0的情况