1 扩展欧几里德算法
扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式: ax+by=gcd(a,b)=d (解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。
gcd: 最大公约数 greatest common divisor
关键点
gcd(a,b)=gcd(b,amodb)
假设
a=kb+r
, d 是 a, b的公约数
那么 因为
r=kb−a
所以r|d , 因此 d也是(b,r)即(b,a mod b)的公约数。
反向证明同上。
// gcd 求解代码
// ax + by = d
int x, y, d;
void gcd(int a, int b) {
if (b == 0) {
x = 1; y = 0; d = a;
return;
}
gcd(b, a%b);
int temp = x;
x = y;
y = temp - a/b*y;
return;
}
关于迭代的x,y变换
一一对应,
2 青蛙的约会
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。 (可见网聊是多么不靠谱啊,可怜的小青蛙)
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。
输入
输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。
输出
输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行”Impossible”
思路
在两只青蛙碰见时会有
其中
k
为跳的次数
令
x=k,a=m−n,y=α,b=L,D=y−x
可得
ax+by=D
注意,此处D不一定是a,b的公约数
下面这段推导参考了圣剑的博客
不过没关系,先用扩展欧几里得得出
ax+by=gcd(a,b)
的一个解
x0,y0
;再观察两个式子:
ax+by=D
ax0+by0=d
(令
d=gcd(a,b)
)
将上面的式子两边都除以
d
,得:
因为d为a,b的最大公约数,所以a/d,b/d为整数,所以D/d必定为整数,否则方程无解;
将下面式两边都乘以d/D与上式比较:
ax+by=D
ax0D/d+by0D/d=D
得 x=x0D/d,y=y0D/d
扩展解
对于方程
ax0/d+by0/d=1
因为d是a,b的gcd, 所以a/d b/d 互质,那么若有
则
故而方程
ax/d+by/d=1
的解为
由于
x=x0D/d,y=y0D/d
则本题的解
k=x=(x0+u(b/d))(D/d)
故而其最小解为
k=(x0D/d)mod(b/d)
3 C++实现
//
// main.cpp
// POJ_1061_青蛙的约会
//
// Created by 黄成林 on 16/3/24.
// Copyright © 2016年 黄成林. All rights reserved.
//
/**
* url: http://poj.org/problem?id=1061
*/
#include <cstdio>
long long x0, y0, d;
void gcd(long long a, long long b) {
if (b == 0) {
x0 = 1; y0 = 0; d = a;
return;
}
gcd(b, a%b);
long long temp = x0;
x0 = y0;
y0 = temp - a/b*y0;
return;
}
int main(int argc, const char * argv[]) {
long long x, y, m, n, L;
while (scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &L) != EOF) {
long long a = m - n;
long long b = L;
long long D = y - x;
if (a < 0) { a = -a; D = -D; }
gcd(a, b);
if (0 != D%d) {
printf("Impossible");
} else {
long long k = x0 * D / d;
long long mod = L / d;
if (k >= 0) {
k %= mod;
} else {
k = k%mod + mod;
}
printf("%lld", k);
}
}
return 0;
}