北大acm 1061解题报告--欧几里得算法及其应用

青蛙的约会
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 81739   Accepted: 14151

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
    这一题初看起来很简单,但是当测试的数据比较大的时候,传统的简单算法会耗时,甚至对于某些测试数据不能快速判断出来。下面我们介绍欧几里得,扩展的欧几里得算法。
    
  
  
  
  

欧几里得算法可能是世界上最早的算法之一,是由古希腊伟大的数学家欧几里得提出的用来计算两个正整数最大公约数(GCD)的算法。在“来自圣经的算法”的网络评选中,其位居榜首,可见该算法的无穷魅力。     下面让我们去体会GCD算法的魅力:

定理一,  (GCD递归定理)对任意非负的整数a和任意的正整数b,              GCD(a,b) = GCD(b, a mod b)    引理:如果 a | b 且 b | a,那么有 a = b 或者 a = -b    证明:(1) a | b ,则存在整数 m ,使得 b = m* a 。等式两边都添加绝对值我们得到:|b| = |m| * |a| ,又有b | a,知b 不等于 0 ,那么显然 m 不为0. 我们可得 |b| = |m| * |a| >= 1 * |a| = |a|。     (2)同理可得:|a| >= |b|    综合(1)、(2)可得:|a| = |b| ,即a = b 或者 a = -b。

   下面我们来证明GCD递归定理:    思路:如果我们可以证明GCD(a,b) 和GCD(b,b mod a)可以相互整除,这样我们根据上面的引理便可得到 GCD(a,b) = GCD(b, b mod a)

   设 d = GCD(a,b),那么有 d | a 和 d | b。那么由整除定理可得 (a mod b) = a - q * b,其中q = a/b 的向下取整值。那么我们得到 (a mod b) 为 a 和 b的一个线性组合,那么有 d | a, d | b 可得 d |(a mod b) 。同时又有 d | b, 那么 d | GCD(b, a mod b),即: GCD(a ,b )| GCD(b,a mod b)。 同理我们可以得到:GCD(b,a mod b) | GCD(a,b)。 综上所述,加上我们的引理: GCD(a , b) = GCD( b,a mod b)。

   欧几里得算法的代码实现:

//迭代版本  

int ITERATIVE-GCD(int a, int b) {
   int r = a % b;
   while (r) {
     a = b;
     b = r;
     r = a % b;
   }
   return b;
}     
//递归版本

int RECURSIVE-GCD(int a, int b) {
   if (b == 0) return a; else return RECURSIVE-GCD(b, a % b);
}

    扩展的欧几里得:
int Euclid_Extended(int a,int b,int &x0,int &y0)  
{  
     int t,d;  
      if (b==0) {
        x0=1; y0=0;
         return a;
      }
      d=Euclid_Extended(b,a%b,x0,y0);  
      t=x0;  
      x0=y0;  
      y0=t-a/b*y0;  
      return d; 
 }
 函数返回值为gcd(a,b),并顺带解出ax+by=gcd(a,b)的一个解x0,y0,
 
 对于不定方程ax+by=c的通解为:
 x=x0*c/d+b/d*t
 y=y0*c/d-a/d*t
 
 当c%gcd(a,b)!=0时,不定方程无解.
 所以对于这一题:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <fstream>
using namespace std;
long long Euclid_Extended(long long a,long long b,long long &x0,long long &y0)  //扩展欧几里德算法,函数返回值为gcd(a,b)                                               
{                                                 //并顺带解出ax+by=gcd的一个解x0,y0
	long long t,d;  
	if (b==0) {
		x0=1; y0=0;
		return a;
	}
	d=Euclid_Extended(b,a%b,x0,y0);  
	t=x0;  
	x0=y0;  
	y0=t-a/b*y0;  
	return d; 
}

int main()
{
	long long x,y,m,n,L;
	long long a,b,c,x0,y0;
	//ifstream fin("e:\\data2.txt");
	//ofstream fout("e:\\1.txt");
	//while(fin>>x>>y>>m>>n>>L)
	while(cin>>x>>y>>m>>n>>L)
	{	
		a=n-m;
		b=L;
		c=x-y;
		long long d=Euclid_Extended(a,b,x0,y0);
		if (c%d!=0)
		{
			cout<<"Impossible"<<endl;
			//fout<<"Impossible"<<endl;
		}
		else
		{
			long long k;
			k=x0*c/d;
			long long t=(b/d)>0?(b/d):(-b/d);
			if (k<0)
			{
				while (k<0)
					k+=t;
			}
			if (k>0)
			{
				while(k>0)
					k-=t;
				k+=t;
			}
			cout<<k<<endl;
			//fout<<k<<endl;
		}	
	}
	return 0;
}
参考:http://blog.csdn.net/zjsxzjb/article/details/6262667
参考:http://blog.tianya.cn/blogger/post_read.asp?BlogID=3880799&PostID=35359648

你可能感兴趣的:(算法,北大,1061,欧几里得)