Euclid's Game
Euclid's Game
Description
25 7
11 7
4 7
4 3
1 3
1 0
an Stan wins.
Input
Output
Sample Input
34 12 15 24 0 0
Sample Output
Stan wins Ollie wins
分析:
【解法一】
大致题意:给定两堆石子,二人轮流取子,要求只能从石子数目较大的那一堆取子,取子的数目只能是另一堆石子数目的倍数.最终使得某一堆数目为零的一方为胜.
就先手角度分析一下,可能出现的几种典型情况:
1、任何一堆为1,或一堆的数目可以除尽另一堆,先手都赢
2、在不是第一种情况下,两堆数目相差一,先手必败;因为先手取了后,对方就会面临刚说的情况1,则先手必败
3、在不是第一种情况下,一堆的数目是另一堆的两倍减一,那先手必赢;先手取了后,对方就将面临情况2,对方必败
从上面的信息,可以延伸想想,当数目大于了两倍后,先手就可以通过取较小堆倍数方法将局势控制在两倍内来讨论,也就是说讨论两倍以上的数目就没啥意义了
继续讨论(N>=M):
1、N<2*M-1时,先手别无选择,只能使之变为 N-M,M 局面
2、N>2*M-1时,先手可使之变为N%M,M 或N%M+M,M两种局面之一,其中有且只有一个必败局面(由以上典型情况的1、2、3可看出)
3、开始说如果N>2*M-1时,先手就控制局势了,确实如此,如果N>2*M-1时,先手就必赢
证明:
假设N%M=k,如果(k,M)是必败态,那作为先手就可将N减成k,使对方面临必败态;如果(k,M)是必胜态,那先手就可将N减成M+k,而对方别无选择,只能取出M,使得先手面对(k,M)必胜态,还是先手赢
4、这里说的N>2*M-1,要求N、M互质,因为对于M,N有公因数的情形,可以同时除以其公因数而不影响结果.
证明:我们取15和21作证明,除以公因数后就是(5,7)
(15,21)->(15,6)->(9,6)->(3,6)->(3,0)
(5,7)->(5,2)->(3,2)->(1,2)->(1,0)
可见效果是一样的
接下来就就写代码吧
# include<iostream> using namespace std; void swap(long long &p1,long long &p2)//由于分别是a、b的别名,则a、b也同步跟着改变 { long long temp; temp=p1; p1=p2; p2=temp; } long long solve(long long n,long long m) { if(n<m)swap(n,m); if(m==n||m==1)return 1; else if(n==m+1&&m) return 0; else if(n>=2*m-1) return 1; else return !solve(n-m,m);//对手将面临的状态,所以要加"!"号 } int main() { long long n,m,r,x,y; while(cin>>n>>m&&(n||m)) { if(n<m)swap(n,m); x=n; y=m; while(y) { r=x%y; x=y; y=r; } n/=x; m/=x; if(solve(n,m)) cout<<"Stan wins"<<endl; else cout<<"Ollie wins"<<endl; } return 0; }
【解法二】
网上说了种更绝的方法http://blog.renren.com/share/320989204/2013759876
用G(x,y)(其中x<=y)来表示两堆石子分别为x,y的情形;也就是说,写在括号前面的那个小一点,后面的那个大一点
在 G(a,b) 情形下 如果a==b,则S必胜,如果 b>=2a,S必胜,否则有a<b<2a,情形转为 G(b-a,a) (要注意到前面S只有一种选择),如果在两倍内,a>2(b-a),则O必胜,否则O也只有一种选择,将较大的石子那一堆减去小石子的数目,情形转为S面临的G(2*a-b,b-a),如果2*a-b>=2*(b-a),则。。。否则是O将面临的。。。如此等等,我们写个表下来:
G(a,b) S b>=2a
G(b-a,a) O a>=2(b-a)
G(2a-b,b-a) S b-a>=2(2a-b)
G(2b-3a,2a-b) O 2a-b>=2(2b-3a)
G(5a-3b,2b-3a) S 2b-3a>=2(5a-3b)
G(5b-8a,5a-3b) O 5a-3b>=2(5b-3a)
........ ... ......
其中S或O为是哪一个人面临此种状态
把最后的不等式全化一下简就看出来了
b>=2a
2b<=3a
3b>=5a
5b<=8a
8b>=13a
13b<=21a
......
要注意到必胜态是对于S和O相间的,O的必胜态为S的必败态,则有对于S,b/a>=2为必胜,b/a<=3/2为必败,b/a>=5/3为必胜,b/a<=8/5为必败,b/a>=13/8为必胜,b/a<=21/13为必败,在数轴上将必胜态的向右标,必败态向左标,发现他们最终是趋向于一个数字的。显然为fib(n)/fib(n-1)在趋向无穷大时的值。由fib通项公式可知这个极值为1.618多一点点,即黄金分割加上1(具体见http://www.cnblogs.com/jiangjun/articles/2743263.html)。说说到黄金分割点,让我不由想起了威佐夫博弈中的黄金分割点的推导。
也就是说当b/a的值在此值右边时,为必胜,左边时为必败,代码如下:
# include<iostream> # include<cmath> using namespace std; const double eps=1e-6; const double p=(sqrt((double)5)+1)/double(2); void swap(double &p1,double &p2)//由于分别是a、b的别名,则a、b也同步跟着改变 { double temp; temp=p1; p1=p2; p2=temp; } int main() { double n,m; while(cin>>n>>m&&(n||m)) { if(n<m)swap(n,m); if(n-m<eps||p<n/m)//在该点以右或者相等都必胜 cout<<"Stan wins"<<endl; else cout<<"Ollie wins"<<endl; } return 0; }
Euclid's Game(又)
| Time Limit: 1000MS | Memory Limit: 65535KB |
| Submissions: 1143 | Accepted: 299 |
Sample Input
3 1
Sample Output
A
分析:
本题大意:一开始黑板上有两个不同的数,两个玩家取两数的差,如果这个差不同于黑板上任何数,就写下来,轮到对方写;不能写出数的的一方为输家:A先写
思路:①这道题也相当于在多的一堆中取数,和上面一道题类似,那也可以做类似的互质处理,也用{21,15}讨论:
{21,15}->{21,15,6}->{21,15,6,9}->{21,15,6,9,3}.....->{21,15,6,9,3,18,12}
{7,5}->{7,5,2}->{7,5,2,3}->{7,5,2,3,1}......->{7,5,2,3,1,6,4},效果一样,因此可以用欧几里得算法化简
②罗列几种互质情况观察{m,n},m<n,且互质:
{1,2}可以写0个:
{1,3}可以写1个:
{2,5}可以写3个:
{3,7}可以写5个:
{3,8}可以写6个:
规律可见:个数都是n-2;而对A而言,偶数个就必输,奇数个必赢,即只看n%2是否为1的问题
现在问题就是不是都满足个数n-2呢?要想个数都为n-2,只要出现1就一定满足了(为什么这么说?因为一旦出现1,那么比n小的所有数都可以被减出来得到,也就是说比n小的数有n-1个,再除去最初给出的另一个数就剩n-2个数了)
那是不是任意的两个互质数都能这样减,得到1呢?
这里引入一个互质数的性质:整数a和b互质当且仅当存在整数x,y使得xa+yb=1。 或者,一般的,有存在整数x,y使得xa+yb=d,其中d是a和b的最大公约数。(贝祖定理)
有这个定理和性质后,我们继续讨论就容易多了。由于a、b>1,则x、y一定是一正一负,那xa+yb就可以写成相减的形式,如:
{5,7}->3*5+(-2)*7=1->5-(7-5)-(7-5)=1;
{7,11}->(-3)*7+2*11=1->(11-7)-[7-(11-7)]=1;
这样看来,任何两个互质数都能按这个方法相减,得到1.
那问题就解决了,代码简单如下:
#include<iostream> using namespace std; int main() { int n,m,r,x,y; cin>>n>>m; x=n; y=m; while(y)//先用欧几里得算法做互质处理 { r=x%y; x=y; y=r; } n/=x; if (n%2) cout<<"A"<<endl; else cout<<"B"<<endl; return 0; }
浙公网安备 33010602011771号