zoukankan      html  css  js  c++  java
  • POJ 1061 青蛙的约会 (扩展欧几里得算法)

    题目链接

    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

    分析:

    开始用了暴力枚举,后来一看数据这么大,估计肯定超时,无奈上网搜索了一下,考察的是扩展欧几里德算法,还有比较大的整数_int64的处理。

    设经过s步后两青蛙相遇,则必满足该等式:(x+ms)-(y+ns)=kl(k=0,1,2....)
    将等式进行变形得:(n-m)
    s+kl=x-y
    令n-m=a,k=b,x-y=c,即原式可以转换为:a
    s+b*l=c
    若上式存在整数解,则两青蛙能相遇,否则不能。

    首先想到的一个方法是用两次for循环来枚举s,l的值,看是否存在s,l的整数解,若存在则输入最小的s,但显然这种方法是不可取的,谁也不知道最小的s是多大,如果最小的s很大的话,超时是明显的。

    其实这题用欧几里德扩展原理可以很快的解决,先来看下什么是欧几里德扩展原理:
    欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数。其计算原理依赖于下面的定理:gcd(a,b) = gcd(b,a mod b)
    证明:a可以表示成a = kb + r,则r = a mod b
       假设d是a,b的一个公约数,则有d|a, d|b,而r = a - kb,因此d|r,所以d是(b,a mod b)的公约数
       假设d 是(b,a mod b)的公约数,则d | b , d |r ,但是a = kb +r,因此d也是(a,b)的公约数
       因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证

    欧几里德算法就是根据这个原理来做的,其算法用C++语言描述为: 

    int Gcd(int a, int b)
    {
          if(b == 0)
              return a;
        return Gcd(b, a % b);  
    }
    

    也可以写成迭代的形式:

    int Gcd(int a, int b) 
    {
        while(b != 0)
        {
            int r = b;
            b = a % b;
            a = r;
        }
        return a;
    }
    

    补充: 扩展欧几里德算法是用来在已知a, b求解一组x,y使得ax+by=Gcd(a,b)(解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。下面是一个使用C++的实现:

    int exGcd(int a, int b, int &x, int &y) 
    {
        if(b == 0)
        {
            x = 1;
            y = 0;
            return a;
              
        }
        int r = exGcd(b, a % b, x, y);
        int t = x;
        x = y;
        y = t - a / b * y;
        return r;  
    }
    

    把这个实现和Gcd的递归实现相比,发现多了下面的x,y赋值过程,这就是扩展欧几里德算法的精髓。
    可以这样思考:
    对于a' = b, b' = a % b 而言,我们求得 x, y使得 a'x + b'y = Gcd(a', b')
    由于b' = a % b = a - a / b * b (注:这里的/是程序设计语言中的除法)
    那么可以得到:a'x + b'y = Gcd(a', b') ===>
      bx + (a - a / b * b)y = Gcd(a', b') = Gcd(a, b) ===>
      ay +b(x - a / by) = Gcd(a, b)
    因此对于a和b而言,他们的相对应的p,q分别是 y和(x-a/b
    y).
      
    在网上看了很多关于不定方程方程求解的问题,可都没有说全,都只说了一部分,看了好多之后才真正弄清楚不定方程的求解全过程,步骤如下:
    求a * x + b * y = n的整数解。
    1、先计算Gcd(a,b),若n不能被Gcd(a,b)整除,则方程无整数解;否则,在方程两边同时除以Gcd(a,b),得到新的不定方程a' * x + b' * y = n',此时Gcd(a',b')=1;
    2、利用上面所说的欧几里德算法求出方程a' * x + b' * y = 1的一组整数解x0,y0,则n' * x0,n' * y0是方程a' * x + b' * y = n'的一组整数解;
    3、根据数论中的相关定理,可得方程a' * x + b' * y = n'的所有整数解为:
    x = n' * x0 + b' * t
    y = n' * y0 - a' * t
    (t为整数)
    上面的解也就是a * x + b * y = n 的全部整数解。

    代码:

    #include<stdio.h>
    #include<iostream>
    using namespace std;
    #define LL long long
    LL exgcd(LL a,LL b,LL &x,LL &y)
    {
        if(!b)
        {
            x=1;
            y=0;
            return a;
        }
        else
        {
            LL t=exgcd(b,a%b,y,x);
            y-=(a/b)*x;
            return t;
        }
    }
    LL x,y,m,n,l;
    LL a,b,c;
    int main()
    {
        scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l);
        a=m-n;
        b=l;
        c=y-x;
        LL gcd=exgcd(a,b,x,y);
        if(c%gcd) printf("Impossible
    ");
        else
        {
            x*=1LL*c/gcd;
            printf("%lld",(x%b+b)%b);
        }
        return 0;
    }
    
  • 相关阅读:
    linux驱动开发学习一:创建一个字符设备
    如何高效的对有序数组去重
    找到缺失的第一个正整数
    .NET不可变集合已经正式发布
    中国人唯一不认可的成功——就是家庭的和睦,人生的平淡【转】
    自己动手搭建 MongoDB 环境,并建立一个 .NET HelloWorld 程序测试
    ASP.NET MVC 中如何用自定义 Handler 来处理来自 AJAX 请求的 HttpRequestValidationException 错误
    自己动手搭建 Redis 环境,并建立一个 .NET HelloWorld 程序测试
    ServiceStack 介绍
    一步一步实战扩展 ASP.NET Route,实现小写 URL、个性化 URL
  • 原文地址:https://www.cnblogs.com/cmmdc/p/8757474.html
Copyright © 2011-2022 走看看