POJ 1061 扩张欧几里得问题

题目大意:

  有两只青蛙A和B,住在同一纬线上。它们分别从坐标x和y出出发。青蛙A每次能跳跃m米,青蛙B每次能跳跃n米,A和B每次都在同一之间跳跃。设地球的纬线长度为L。

  问A和B是否能够相遇(在同一时间到达同一坐标),如果能够相遇,那么需要跳跃多少次?

解题思路:

  利用欧几里得扩展式子。

  我们这道题最后是要求x + k*m = y + k*n + pL。其中k、p为整数,需要确定。

  将上面的等式进行简单的变换,可得 

  (x-y) = k(n-m) + pL

  设a=n-m,b = L,c=x-y,则上面的等式变为:

  a*k + b*p = c

  在介绍上面等式的解法之前,我们先介绍利用欧几里得的方法求解最大公约数。

  在我们求解最大公约数的时候,我们用到了欧几里得方法,主要是以下的递归式:

  gcd(a, b) = gcd(b, a%b)。

  参考《算法导论》上数论的那一章,我们现在对其进行证明。

  要证明gcd(a, b) = gcd(b, a%b)。只需要证明gcd(a, b) | gcd(b, a%b) && gcd(b, a%b)|gcd(a, b)。

  a%b可以用a,b线性表示。a % b = a - floor(a/b)*b。floor()表示向下取整。

  设d = gcd(a, b),因为a%b是a和b的线性组合,所以可得d|(a%b)。

  又因为d|b。所以可得d|gcd(b, a%b)。即gcd(a, b) | gcd(b, a%b)。

  证明gcd(b, a%b)|gcd(a, b)的过程与上面的类似,这里就不在叙述了。

  有了上面的递推式,求解最大公约数的程序可以如下这样写:

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

  

  为了求解上面的式子:a*k + b*p = c,我们先求解如下的式子

  a*k + b*p = d,其中d = gcd(a, b)。

  考虑下面的式子:

  d = a*k + b*p = b*k` + (a%b)*p`

  又因为a%b =  a - floor(a/b)*b

  所以,d = b*k` + (a - floor(a/b)*b)*p` = a*p` + b*(k` - floor(a/b)*p`) = a*k + b*p。

  所以可得 k = p`; p = k`-floor(a/b)&p`。

  

  于是扩展的欧几里得代码可以写成如下: 

int extendedEuclid(int a, int b, int &k, int &p)
{
    if (b == 0) {
        k = a;
        y = 1;
        return;
    }

    extendedEuclid(b, a%b, k, p);
    int temp = k;
    k = p;
    p = (temp - a/b)*p;
}

  在求出a*k + b*p = d的解k后,在等式两边同时乘以c/d。得到 k = k*c/d, 即得到a*k + b*p = c的一个解。

  然后再计算k = (k%b + b)%b。即得到原问题的一个解。

  最后AC的代码如下(需要注意的是这道题的数的范围可能会超出整数,因此要使用long long类型):

  Memory: 132KTime: 0MS

  Language: C++Result: Accepted

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string>
#include <iostream>
#include <stack>
using namespace std;

typedef long long LL;


int gcd(LL a, LL b)
{
    if (b == 0)
        return a;
    else
        return gcd(b, a%b);
}

void extendedEuclid(LL a, LL b, LL &x, LL &y)
{
    if (b == 0) {
        x = a;
        y = 1;
        return;
    }

    extendedEuclid(b, a%b, x, y);
    LL temp = x;
    x = y;
    y = temp - (a/b)*y;
}

int main(int argc, const char *argv[])
{
    //freopen("in.txt", "r", stdin);

    LL x, y, m, n, L;
    LL a, b, c;
    LL k , j, d;

    scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &L);

    // a*k + b * j = d
    a = n - m;
    b = L;
    c = x - y;
    if (a < 0) {
        a = -a;
        c = -c;
    }

    int r = gcd(a, b);
    if (c % r != 0) {
        printf("Impossible\n");
    }
    else {
        a = a / r;
        b = b / r;
        c = c / r;
        extendedEuclid(a, b, k, j);
        k = ((k*c)%b + b)%b;
        printf("%lld\n", k);
    }

    return 0;
}

POJ上与此题类似的题目还有POJ2115POJ2142,有兴趣可以一起刷了.

原文地址:https://www.cnblogs.com/lovesaber/p/2762588.html