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上与此题类似的题目还有POJ2115 和 POJ2142,有兴趣可以一起刷了.