POJ 1061 解题报告

这道题是Extended Euclidean Algorithm的应用。可以看

链接1(extended euclidean algorithm):http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm

链接2(Bezout's Identity):http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity

这里的代码是按照wikipedia上链接1的描述写的。


首先假设当两只青蛙相遇时,都跳了K步(根据题意,它们跳得步数是一样的),并且第一只青蛙比第二只青蛙多跳了c圈(c可以为负数),这样,我们有:

x + Km - (y + Kn) = cL ,即:(x - y) + K(m - n) = cL,亦即:

K(n - m) + cL = (x - y)      (以下称作原方程)

这样就转化为了一个可以应用Extended Euclidean Algorithm的问题。

Extended Euclidean Algorithm可以求出ax + by = gcd(a, b)的一个整数解(x0, y0)。然后根据链接2,我们知道这个方程的所有整数解为(x, y) = (x0 + kb/g, y - ka/g)。(这点比较好验证,带进去后面部分就都消掉了)。其中k是任意整数,g=gcd(a,b).

我们让a = n - m, b = L,那么我们可以根据上述extend euclidean algorithm得到一组解(x0, y0)。这个算法还可以附带得到g = gcd(a, b)。(所以不用单独跑gcd算法)。

很容易看出,既然左边能被g整除,那么右边的(x-y)也必须能够被g整除,否则原方程无解。

我们将原方程右边记做R,即R = x - y。

否则,我们可以得到这个方程的所有解: (x, y) = (R / g * x0 + kb/g, R/g * y0 + kb/g)。

按照问题,我们只关心第一项,即K = R / g * x0 + kb/g。我们要求的是这样一组K中最小的非负整数。

做法是先让K=0,求出k(这样的整数k不一定存在,但这样我们就可以求出使得K最接近0的k),然后求K。

k = (- R/g *x0) / (b/g)

然后K = R / g * x0 +  (- R/g *x0) / (b/g) * b/ g。

注:上述公式中都是整数运算。

我们用P = R/g, Q = b / g,x0 = mQ + r带入上述简化表达式,有:

K = P * (mQ + r) - P (mQ + r) / Q * Q = P*(mQ + r) - P m Q = P*r = P * (x0 % Q) = (P*x0) % Q。

即K是P*x0对Q的余数。

剩下的值得注意的就是让Q为正数就好了(可以直接取Q的绝对值,因为这时相当于k的绝对值变了)。


程序如下:

1061Accepted216K0MSC++1906B

/* 
ID: thestor1 
LANG: C++ 
TASK: poj1061 
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <limits>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;

void extend_gcd(long long a, long long b, long long &g, long long &x0, long long &y0)
{
	long long r0 = a, r1 = b, s0 = 1, s1 = 0, t0 = 0, t1 = 1;
	long long q2, r2, s2, t2;
	while (r1 != 0)
	{
		q2 = r0 / r1;
		r2 = r0 - r1 * q2; // r0 % r1
		s2 = s0 - s1 * q2;
		t2 = t0 - t1 * q2;

		r0 = r1, r1 = r2;
		s0 = s1, s1 = s2;
		t0 = t1, t1 = t2;
	}
	// cout << a << " * " << s0 << " + " << b << " * " << t0 << " == " << r0 << "(GCD(" << a << ", " << b << "))" << endl;
	g = r0;
	x0 = s0;
	y0 = t0;
}

int main()
{
	long long x, y, m, n, L;
	
	while (cin >> x >> y >> m >> n >> L)
	{
		// extend_gcd(240, 46);

		// x + km - (y + kn) = cL 
		// =>
		// (x - y) + k(m - n) = cL
		// =>
		// k(n - m) + cL = (x - y)
		long long a = n - m, b = L, R = x - y;
		long long g, x0, y0;
		extend_gcd(a, b, g, x0, y0);
		// cout << "a: " << a << ", b: " << b << ", R: " << R << endl;
		// cout << "g: " << g << ", x0: " << x0 << ", y0: " << y0 << endl;

		if (R % g != 0)
		{
			cout << "Impossible" << endl;
		}
		else
		{
			long long k = R / g * x0;
			long long add = b / g;
			if (add < 0)
			{
				add = -add;
			}
			cout << (k % add + add) % add << endl;
		}
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值