Poj 1061 青蛙的约会 数论 欧几里得 求余方程

本文探讨了两只在线相识的青蛙如何通过跳跃在同一条纬度线上相遇的问题。利用数论中的经典求余方程Linearcongruence,结合扩展欧几里得算法,解析了如何确定两青蛙能否及何时会面。

Description

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。 
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。 

Input

输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

Output

输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"

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);
		}
	}
};

做完这道题之后,感觉数论的求余方程和扩展欧几里得的知识点都有更加深入的理解了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值