Euclid's Game

Euclid's Game

 

Description 

Two players, Stan and Ollie, play, starting with two natural numbers. Stan, the first player, subtracts any positive multiple of the lesser of the two numbers from the greater of the two numbers, provided that the resulting number must be nonnegative. Then Ollie, the second player, does the same with the two resulting numbers, then Stan, etc., alternately, until one player is able to subtract a multiple of the lesser number from the greater to reach 0, and thereby wins. For example, the players may start with (25,7):
         25 7
11 7
4 7
4 3
1 3
1 0

an Stan wins.  

Input 

The input consists of a number of lines. Each line contains two positive integers giving the starting two numbers of the game. Stan always starts.

Output

For each line of input, output one line saying either Stan wins or Ollie wins assuming that both of them play perfectly. The last line of input contains two zeroes and should not be processed. 

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>=2aS必胜,否则有a<b<2a,情形转为  G(b-a,a)   (要注意到前面S只有一种选择),如果在两倍内,a>2(b-a),则O必胜,否则O也只有一种选择,将较大的石子那一堆减去小石子的数目,情形转为S面临的G2*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)

            ........        ...      ......

其中SO为是哪一个人面临此种状态

把最后的不等式全化一下简就看出来了

                     b>=2a

                     2b<=3a

                     3b>=5a

                     5b<=8a

                     8b>=13a

                     13b<=21a

                      ......

要注意到必胜态是对于SO相间的,O的必胜态为S的必败态,则有对于Sb/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

 

Description

Starts with two unequal positive numbers (M,N and M>N) on the board. Two players move in turn. On each move, a player has to write on the board a positive number equal to the difference of two numbers already on the board; this number must be new, i.e., different from all the numbers already on the board. The player who cannot move loses the game. Should you choose to move first or second in this game?

According to the above rules, there are two players play tihs game. Assumptions A write a number on the board at first, then B write it.

Your task is write a program to judge the winner is A or B.

 

Input

Two unequal positive numbers M and N , M>N (M<1000000)

 

Output

A or B

 

Sample Input

3 1

Sample Output

 A

 

Hint

Source
algorithm text book

 

分析:

本题大意:一开始黑板上有两个不同的数,两个玩家取两数的差,如果这个差不同于黑板上任何数,就写下来,轮到对方写;不能写出数的的一方为输家: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;
}

 

posted on 2012-10-27 21:00  即为将军  阅读(904)  评论(0)    收藏  举报

导航