通过POJ1061青蛙的约会来谈拓展欧几里德算法

分类:数论,扩展欧几里德算法,同余方程
作者: ACShiryu
时间:2011-8-3
青蛙的约会
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 62869 Accepted: 9818

Description

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。 
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。 

Input

输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

Output

输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"

Sample Input

1 2 3 4 5

Sample Output

4
对于题目中的数据,详见下表

故,可得两只青蛙跳四次就可以在点3处相遇

对于这道题可以知道,当他们相遇时距离原点的位移是相等的,则假设青蛙跳了t次后,则他们相对于原点的位移是

A:(x+mt)%l

B:(y+nt)%l

则可以列方程(x+mt)-(y+nt)=cl  ( c为整数)

则变形得 (m-n)t-cl=y-x;

题目要求的就是要使等式成立时最小时的正整数t

在解决这个问题前,我们首先就应该知道什么是扩展欧几里德算法

找出一对整数(x,y),使得ax+by=gcd(a,b)。

注意,这里的x和y不一定是正数,也可能是负数或者0.

下面是扩展欧几里德算法的源程序:(参考刘汝佳的《算法竞赛入门经典》第179页)

 1 void gcd ( int a , int b , int &d , int &x , int &y )
2 {//a,b分别代表方程的系数,d返回a,b的最大公约数,x,y返回对应的解
3 if ( ! b )//当b等于0的时候,方程就变成了ax=gcd(a,0)=a,所以此时明显可以得到方程的解为x=1,y=0,此时d就为a
4 d = a , x = 1 , y = 0 ;
5 else
6 {//递归求方程的解,等下证明
7 gcd ( b , a % b , d , y , x ) ;
8 y -= ( a / b ) * x ;
9 }
10 }

  书上对该算法没有给出证明,只有“用数学归纳法并不难证明算法的正确性”一笔代过,现在,去我们就来证明该算法的正确性

当b=0时很好理解,详见上面的注释

关键是当b=/=0,则我们先来假设方程的ax+by=gcd(a,b)=d的一个正整数解为x1,y1;别怀疑,这个方程一定有解

则有ax1+by1=gcd(a,b)   (1)

又对于方程bx +(a mod b)y =gcd (b ,a mod b )有解x2,y2(假设)

则有bx2+(a mod  b)y2=gcd ( b,a mod b) = gcd(a, b)   (2)

又a mod b = a - (a/b)*b;

则(2)式变为bx2+(a-(a/b)*b)y2=gcd(a,b);

即 ay2 + b(x2-(a/b)*y2) = gcd (a,b)    (3) ;

对比(1)(3)得

x1=y2    ;     y1 =  x2 - (a/b)*y2

故,ax+by=gcd(a,b)的解只需要在方程bx +(a mod b)y =gcd (b ,a mod b )的解的基础上进行简单的运算就变成原来方程的解,因为gcd不断递推时会有b=0的情况出现,故可以通过递推来得到方程的解

然后得出了关于方程ax+by=gcd(a,b)的解x0,y0,

但如何要求题目所要求的解了;

假设方程是ax+by=c;

现在我们已经知道了ax+by=gcd(a,b)的解x0,y0,即ax0+by0=gcd(a,b);

则等式两边同乘以c/gcd(a,b)则得

ax0*c/gcd(a,b)+by0*c/gcd(a,b)=c;(则可知人如果c不是gcd(a,b)的倍数则无解)

故可以得到原方程的一个解是x1=x0*c/gcd(a,b),y1=y0*c/gcd(a,b),

再根据下面的结论就可以很好的得出此题的答案了

设a,b,c为任意整数。若方程ax+by=c的一组整数解为(x0,y0),则它的任意整数解都可以写成(x0+kb',y0-ka'),其中a'=a/gcd(a,b),b'=b/gcd(a,b),k为任意整数

  关于上面的结论很好证明,此处略。

刚开始的时候没有注意到怎样就解答系,WA了两次

参考代码:

 1 #include<iostream>
2 #include<cstdlib>
3 #include<cstdio>
4 #include<cstring>
5 #include<algorithm>
6 #include<cmath>
7 using namespace std;
8
9 void gcd ( __int64 a , __int64 b , __int64 &d , __int64 &x , __int64 &y )
10 {
11
12 if ( ! b )
13 d = a , x = 1 , y = 0 ;
14 else
15 gcd ( b , a%b , d , y , x ) , y -= x * ( a / b ) ;
16 }
17
18 int main()
19 {
20 __int64 s , t , m , n , l ;
21 while ( ~ scanf ("%I64d%I64d%I64d%I64d%I64d" , & s , & t , & m , & n , & l ) )
22 {
23 __int64 a , b , d , ans ;
24 __int64 x , y ;
25 a = l ;
26 b = m - n ;
27 ans = t - s ;
28 if ( b < 0 )
29 b = n - m , ans = s - t ;
30 gcd ( a , b , d , x , y ) ;
31 if ( ans % d )//无解出现的情况
32 printf("Impossible\n") ;
33 else
34 {
35 __int64 tmp = l / d ;
36 ans = ( ans / d * y ) % tmp ;//求出答案,因答案要求最小,故还得对答案的“周期”取余
37 if ( ans < 0 )//如果出现的是负数,就要加上周期
38 ans += tmp ;
39 printf ("%I64d\n",ans);
40 }
41 }
42 return 0;
43 }

你可能感兴趣的:(poj)