扩展欧几里得算法求解不定方程【例 poj 1061】

扩展欧几里得算法是数论当中一种常用的算法,他可以用如下的姿势来表达:

设a, b为不全为0的整数,则存在整数x和y,使得 gcd(a, b) = a*x + b*y。

证明就略去。

树上还有一个拉梅定理:用欧几里得算法计算两个正整数的最大公因子时,所需要的除法次数不会超过两个整数中较小的那个十进制数的倍数的5倍。

拉梅定理的一个推论:求两个正整数a, b, a > b的最大公因子需要O(log2a)3次的位运算。

至于拉梅定理有什么用,暂时还没有研究=—=。

例1 求225和21的最大公因子s,并将s表示成225和21的线性组合。

解:

  gcd(225, 21) = 3

  225 = 10*21 + 15, 21 = 1*15 + 6, 15 = 2*6 + 3, 6 = 2*3 + 0。

  于是我们有 3 = 15 - 2*6 = 15 - 2*(21 - 1*15) = 3*15 - 2*21 = 3*(225 - 10*21) - 2*21 = 3*225 - 32*21。

扩展欧几里得算法的代码实现:

 1 #include <cstdio>
 2 #include <cstring>
 3 #define ll long long
 4
 5 using namespace std;
 6
 7 ll extend_gcd(ll a, ll b, ll &x, ll &y)
 8 {
 9     if(b == 0)
10     {
11         x = 1, y = 0;
12         return a;
13     }
14     else
15     {
16         ll r = extend_gcd(b, a%b, y, x);
17         y -= x*(a/b);
18         return r;
19     }
20 }

  这个函数的时间复杂度为O(logN), 其中N与a, b同阶。函数的参数a, b即为等式当中的两个参数,x和y传引用,可以从过程当中求出x和y,返回值是gcd(a, b)。

  调用后可以求出满足a*x + b*y = gcd(a, b)的各个参数。

  对于形如a*x0 + b*y0 = n的不定方程为了求解x0和y0,可以通过扩展欧几里得先求出满足a*x + b*y = gcd(a, b)的x和y。容易得到,若gcd(a, b)|(x-y),则该不定方程有整数解,否则无符合条件的整数解。得到x和y后,可以通过x0 = x*n / gcd(a, b)求出x0.

  由于在实际问题当中,我们需要的往往是最小整数解,我们可以通过下面的方法求出最小整数解:

      令t = b/gcd(a, b),x是方程a*x + b*y = n的一个特解,则xmin = (x % t + t) % t

下面上例题 poj 1061--青蛙的约会

青蛙的约会

Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 98378   Accepted: 18666

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

Source

浙江

当两只青蛙跳t步后,A的坐标为x+mt-p1L(p1∈Z且x+mt-p1L<L),B的坐标为y+nt-p2L(p2∈Z且y+nt-p2L<L), A和B相遇的充分必要条件是x+mt-p1L = y+nt-p2L。

整理可得 (x-y) + (m-n)t = (p1-p2)L, 即 (n-m)t + (p1-p2)L = x-y

设p = p1 - p2 整理得 (n-m) * t + L * p = x-y

看出a * x + b * y = gcd(a, b)的样子了没?

调用extend_gcd(n-m, L, t, p)可以算出gcd(n-m, L), t, p。之后再用上面的方法算出最小整数解就可以了。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #define ll long long
 4
 5 using namespace std;
 6
 7 ll extend_gcd(ll a, ll b, ll &x, ll &y)
 8 {
 9     if(b == 0)
10     {
11         x = 1, y = 0;
12         return a;
13     }
14     else
15     {
16         ll r = extend_gcd(b, a%b, y, x);
17         y -= x*(a/b);
18         return r;
19     }
20 }
21
22 int main()
23 {
24     ll x, y, m, n, l;
25     while(~scanf("%I64d%I64d%I64d%I64d%I64d", &x, &y, &m, &n, &l))
26     {
27         ll t, p;
28         ll ans = extend_gcd(n-m, l, t, p);
29         if((x-y) % ans)
30             printf("Impossible\n");
31         else
32         {
33             //求最小整数解的算法
34             t = (x-y)*t/ans;                        //首先令x为一个特解
35             t = (t % (l/ans)+(l/ans)) % (l/ans);    //再根据公式计算
36             printf("%I64d\n", t);
37         }
38     }
39 }
时间: 2024-12-16 05:17:58

扩展欧几里得算法求解不定方程【例 poj 1061】的相关文章

扩展欧几里得算法------扩展欧几里德算法

扩展欧几里得算法及其应用 一.扩展欧几里得算法 扩展欧几里得算法:对于不完全为 0 的非负整数 a,b,若gcd(a,b)表示 a,b 的最大公约数,必然存在整数对x,y ,使得 ax+by = gcd(a,b). 算法过程: 设 a>b,当 b=0时,gcd(a,b)=a.此时满足ax+by = gcd(a,b)的一组整数解为x=1,y=0:当a*b!=0 时, 设 a*x1+b*y1=gcd(a,b):b*x2+(a mod b)*y2=gcd(b,a mod b): 根据欧几里得原理知 g

POJ - 1061 青蛙的约会 (扩展欧几里得算法)

Description 两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面.它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止.可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置.不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的.但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的.为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面. 我们把这

POJ 1061 青蛙的约会(扩展欧几里得算法)

http://poj.org/problem?id=1061 思路: 搞懂这个扩展欧几里得算法花了不少时间,数论真的是难啊. 含义:找出一对整数,使得ax+by=gcd(a,b). 接下来看这道题目,(x+mt)-(y+nt)=kl,转换成(n-m)t+kl=x-y. 令a=n-m,b=l,c=x-y,那么上式就变成了at+kb=c,之后就参照上面的算法来计算就行,具体参见代码. 1 #include<iostream> 2 #include<algorithm> 3 #inclu

POJ 1061 青蛙绕地球约会-数论-(解一元一次同余方程+扩展欧几里得算法)

题意:两只青蛙同向跳,起点是x,y,每次分别跳m,n米,地球周长是L,求最少跳几次相遇. 分析: 把式子写好就发现是一个一元一次同余方程.用扩展欧几里得算法来求.这题很基本得会. 代码: #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<queue> #define INF 100000

POJ 2115 for求循环次数-数论-(同余方程+扩展欧几里得算法)

题意:给定for循环的初始值,结束值和增量,还有一个模,求最少的循环次数. 分析: 读完题后应该就知道是一个同余的概念,所以就是解一个一元一次同余方程,像上题一样用扩展欧几里得算法.这题的trick点是k最大为32,那么2^32超出了int,要用long long,所以在1<<k时要这样做:1LL<<k,不然就WA了. 代码: #include<iostream> #include<cstdio> #include<algorithm> #inc

扩展欧几里得算法详解

一:欧几里得算法(辗转相除法) 基本算法:设a=qb+r,其中a,b,q,r都是整数,则gcd(a,b)=gcd(b,r),即gcd(a,b)=gcd(b,a%b). 证明: a可以表示成a = kb + r,则r = a mod b 假设d是a,b的一个公约数,则有 d|a, d|b,而r = a - kb,因此d|r 因此d是(b,a mod b)的公约数 假设d 是(b,a mod b)的公约数,则 d | b , d |r ,但是a = kb +r 因此d也是(a,b)的公约数 因此(a

扩展欧几里得算法的模板实现

我居然现在还记不住扩欧的板子,我太弱啦! 扩展欧几里得算法解决的是这样的问题: 给定一个不定方程组ax+by=gcd(a,b),求他的一组整数解 先给出实现代码 void exgcd(int a,int b,int &x,int &y) { if(!b) { x=1,y=0;//gcd(a,0)显然等于1*a-0*0=a return a; } int ans=exgcd(b,a%b,x,y); int tem=x; x=y; y-=tem-(a/b)*y; return ans;} 但实

欧几里得算法与扩展欧几里得算法_C++

先感谢参考文献:http://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html 注:以下讨论的数均为整数 一.欧几里得算法(重点是证明,对后续知识有用) 欧几里得算法,也叫辗转相除,简称 gcd,用于计算两个整数的最大公约数 定义 gcd(a,b) 为整数 a 与 b 的最大公约数 引理:gcd(a,b)=gcd(b,a%b) 证明: 设 r=a%b , c=gcd(a,b) 则 a=xc , b=yc , 其中x , y互质

noip知识点总结之--欧几里得算法和扩展欧几里得算法

一.欧几里得算法 名字非常高大上的不一定难,比如欧几里得算法...其实就是求两个正整数a, b的最大公约数(即gcd),亦称辗转相除法 需要先知道一个定理: gcd(a, b) = gcd(b, a mod b) (其中a mod b != 0)  或  b (其中a mod b == 0) 证明: 后半部分呢...是废话,于是只要证明前半部分即可. 不妨设g = gcd(a, b),于是有 a = g * A, b = g * B 且 (A, B) = 1 故gcd(b, a mod b) =