POJ1061 青蛙的约会
题目来源:http://poj.org/problem?id=1061
题目大意:
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙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"
Sample Input
1 2 3 4 5
Sample Output
4
终于有一道中文题目了,真是泪流满面T^T..
由题意知,实际上问题是要求解下面的方程:
(1)
其中 t 为未知数,要求的是 t 的非负最小值。上式可变形为: (2)
其中 t 和 k 为未知数。再将上式简化为(3) 的形式。其中, a b c 均为整数常数。数论中的相关理论(具体什么理论我也不知道=。=)保证了:当 c = gcd(a, b)时,该方程的 t 和 k 一定有解。所以如果可以把(2)式转化为 (4) 的形式,则原问题有解,否则无解,应输出“Impossible.”。
接下来说明(4)式的解法--扩展的欧几里得算法。
首先请移步这里回顾一下求两数最大公约数的朴素欧几里得算法。然后, 我们把代码搬过来:
int gcd(int a, int b) { if(b == 0) return a; return gcd(b, a % b); }
所谓的扩展欧几里得算法,代码如下:
int x, y;
int ex_gcd(int a, int b) { if (b == 0) { x = 1; y = 0; return a; } else { int r = ex_gcd(b, a % b); int t = x; x = y; y = t - a / b * y; return r; } }
这里的a, b与前面方程(4)里的a, b对应, x, y对应t, k,返回值为gcd(a, b)。[ax + by = gcd(a, b)]. 我们发现,ex_gcd算法与gcd相比,多了 x 与 y 的赋值过程。只要在纸上演算一下,会发现其实算法很好理解:
1. b == 0 时,对应方程 gcd(a, b) = a, 对应的原方程为:ax + 0y = a, 所以可以赋值 x = 1, y = 0;
2. 当 b != 0 时,因为gcd(a, b) = gcd(b, a % b). 所以存在x'和y'满足:
ax + by = gcd(a, b)
= gcd(b, a % b)
= bx' + (a % b)y'
= bx' + (a - a / b * b)y'
= ay' + b(x' - a / b * y')
于是我们从上面连等式中可以找到对应关系:x = y'; y = x' - a / b * y'. 算法就是利用该递推关系就求出方程(4)的一组解,设之为(x0, y0)。
显然解不是唯一的,而是应该构成一组解系。设gcd(a, b) = d, 则 ax0 + by0 = d, 且有:
于是我们构造出了解系: 其中n为任意整数。
然后对于一般形式的方程 ax + by = c, 则对应着解系. 所以如果 c/gcd(a, b) 是整数,则该方程有整数解,若不是则无整数解。
那么回到题目中,本题实际上求的是满足方程的x中的最小非负值,求得了x0和d后应该是不难得到了。至此应该把原理讲清楚了吧,嗯,上AC代码。
1 //////////////////////////////////////////////////////////////// 2 // POJ1061 Frogs' Dating 3 // Memory: 216K Time: 0MS 4 // Language: C++ Result : Accepted 5 //////////////////////////////////////////////////////////////// 6 7 #include <iostream> 8 9 using namespace std; 10 11 long long x, y; 12 13 long long ex_gcd(long long a, long long b) { 14 if (b == 0) { 15 x = 1; 16 y = 0; 17 return a; 18 } else { 19 long long r = ex_gcd(b, a % b); 20 long long t = x; 21 x = y; 22 y = t - a / b * y; 23 return r; 24 } 25 } 26 27 int main(void) { 28 long long X, Y, m, n, L, c, d, t; 29 cin >> X >> Y >> m >> n >> L; 30 c = X - Y; 31 d = ex_gcd(n - m, L); 32 t = c % d; 33 if (t == 0) { 34 long long k = c / d; 35 x *= k; 36 t = d * x / L; 37 x -= t * L / d; 38 if (x < 0) { 39 x += L / d; 40 } 41 cout << x << endl; 42 } else { 43 cout << "Impossible" << endl; 44 } 45 return 0; 46 }
关于求满足条件的最小非负数的附加解释:
令 ,此时求得的n对应着最接近0的x,如果算出的x小于0,则在它的基础上再加一个周期 b/d 即可。