Description
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。
Input
Output
Sample Input
1 2 3 4 5
Sample Output
4
很傻的题目,却是数论中的经典问题,求余方程问题 Linear congruence.
首先根据题意写出等式:(x + km) % L = (y + kn) % L ;
因为这样的余数方程是可以变化的,举例最好理解了,如:8 % 6 == 2,那么两边减去1得到:7 % 6 == 1,同样是成立的,
所以根据这个性质,可以变化等式为: (k (m-n)) % L = (y - x) % L
这样我们可得到方程式:k(m-n) = y - x(mod L)
其中的k是设需要跳多少步,这个方程可以这么理解:和式子(k(m-n)) % L = y + kn不同的是,前者是方程,后者只是一个计算结果了。
然后问题就转化为一个解余数方程的问题了。
这里注意m-n必须为正数,否则就成了符号相反了,成反方向了。而y-x的正负却不成问题的
解这个方程的步骤:
1 使用扩展欧几里得算法,求得其扩展系数 s, t
2 判断是否有解,如果 g = GCD(a, b), (y - x) % g不能除尽,那么是无解的,例子: 3 * x == 4 (mod 9)是无解的,因为GCD(3, 9) == 3,而4 % 3 == 1,不能除尽,那么是找不到一个整数未知数x,满足方程的,不信,可以试一试手动找找。
3 求余公式为long long ans = (y-x) / g * s ;
怎么来的? 还是举例比较清楚,例如:3x == 4 (mod 7),因为GCD(3, 7) == 1那么欧几里得扩展得:1 = -2 * 3 + 7;系数s == -2;那么-2 * 4 = -8,-8就是一个方程解,但是同时-8 - 7, - 8 - 7 *2, - 8 + 7, - 8 + 7 * 2等等都是解
4 最后要保证求得的解为最小正整数:ans = (ans % L + L ) % L
还不明白就只能参考数论的书本了。英文名:Linear congruence问题。
因为网上也有博客在解析这道题,不过好像都很难讲清楚,毕竟是数学问题就难于说清楚了。其中有一篇解析的非常详细的,不过我看也不是那么清楚,有点绕弯的感觉了。我这里的特点就是使用例子来解析一下,希望更好懂点。
作者 靖心: http://blog.csdn.net/kenden23/article/details/29351167
最后程序:
#include <cstdio>
#include <algorithm>
using namespace std;
class FrogsDate1061_1
{
long long s, t, g; //扩展式子为:sa + tb = g, g为GCD(a, b)的值
void extGCD(long long a, long long b)
{
if (b == 0)
{
s = 1, t = 0;
g = a; //保存结果
}
else
{
extGCD(b, a % b);
long long tmp = s;
s = t;
t = tmp - a / b * t; //逆推公式
}
}
public:
FrogsDate1061_1()
{
long long x, y, m, n, L;
scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &L);
if (m < n)
{
swap(m, n);
swap(x, y);
}
extGCD(m-n, L);
y -= x;
if (y % g) puts("Impossible");
else
{
long long ans = y / g * s; //求余公式
ans = (ans % L + L) % L; //保证为最小正数
printf("%lld\n",ans);
}
}
};
做完这道题之后,感觉数论的求余方程和扩展欧几里得的知识点都有更加深入的理解了。
本文探讨了两只在线相识的青蛙如何通过跳跃在同一条纬度线上相遇的问题。利用数论中的经典求余方程Linearcongruence,结合扩展欧几里得算法,解析了如何确定两青蛙能否及何时会面。

3741

被折叠的 条评论
为什么被折叠?



