首先先来介绍一下什么是拓展欧几里得算法!
我们知道欧几里得算法利用gcd(a,b) = gcd(b , a%b)(非重点,可以百度证明过程)来求解a,b的最大公约数
int gcd( int a , int b)
{
if(b==0) returna;
else returngcd(b, a%b);
}
那什么是拓展欧几里得呢?和欧几里得算法有什么联系?实际上就是在欧几里得算法求公约数的过程中将x, y求出来!下面来说说x, y是什么鬼!
现在我们知道了 a 和 b 的最大公约数是 gcd ,那么,我们一定!!一定!!(数论知识)能够找到这样的 x 和 y 满足: a*x + b*y = gcd 这是一个不定方程,并且是有多解的。那我们能不能找出一条通式来表达所有的可能解呢?是可以的!只要我们找到一组特殊的解 x0 和 y0 那么,我们就可以用x0 和 y0 表示出整个不定方程的通解:
x = x0 + (b/gcd)*t (务必留意)
y = y0 – (a/gcd)*t
为什么不是:
x = x0 + b*t
y = y0 – a*t
b/gcd 是 b 的因子, a/gcd 是 a 的因子是吧?那么,由于 t的取值范围是整数,你说 (b/gcd)*t 取到的值多还是 b*t 取到的值多?同理,(a/gcd)*t 取到的值多还是 a*gcd 取到的值多?那肯定又要问了,那为什么不是更小的数,非得是 b/gcd 和a/gcd ?
注意到:我们令 B = b/gcd , A = a/gcd , 那么,A 和 B 一定是互素的吧?(仔细想想应该不难理解!)所以最小的系数就是 A 和 B 了!
现在,我们知道了一定存在 x 和 y 使得 : a*x +b*y = gcd , 那么,怎么求出这个特解 x 和 y 呢?只需要在欧几里德算法的基础上加点改动就行了。也就是开头说的求解x, y!
我们观察到:欧几里德算法停止的状态是: a= gcd , b = 0 ,那么,这是否能给我们求解 x y 提供一种思路呢?因为,这时候,只要 a = gcd 的系数是 1 ,那么只要 b 的系数是 0 或者其他值(无所谓是多少,反正任何数乘以 0 都等于 0 但是a 的系数一定要是 1),这时,我们就会有: a*1 + b*0 = gcd
这是最终状态,那我们是否可以从最终状态反推到最初的状态呢?上面不懂为什么没关系!往下看!
假设当前我们要处理的是求出 a 和 b的最大公约数,并求出 x 和 y 使得 a*x + b*y= gcd ,而我们已经求出了下一个状态:b 和 a%b 的最大公约数,并且求出了一组x1 和y1 使得: b*x1 +(a%b)*y1 = gcd , 那么这两个相邻的状态之间是否存在一种关系呢?
我们知道: a%b = a - (a/b)*b(这里的 “/” 指的是整除,例如 5/2=2 , 1/3=0),那么,我们可以进一步得到:
gcd = b*x1 + (a-(a/b)*b)*y1
= b*x1 + a*y1 – (a/b)*b*y1
= a*y1 + b*(x1 – a/b*y1)
对比之前我们的状态:求一组 x 和 y 使得:a*x + b*y= gcd ,是否发现了什么?
这里:
x = y1
y = x1 – a/b*y1
那我们怎么知道x1, y1呢?求出x2, y2!!!直到最终状态!
还是不懂具体看看代码,多想想,也许就能懂吧…….
在此之前先来几个定理,实际上就是把上面说的重新简洁地叙述了一遍
以上就是扩展欧几里德算法的全部过程,依然用递归写:
扩展欧几里德有什么用处呢?
求解形如 a*x +b*y = c 的通解,但是一般没有谁会无聊到让你写出一串通解出来,都是让你在通解中选出一些特殊的解,比如下面的青蛙约会!
根据题意,两个青蛙跳到同一个点上才算是遇到了,不是一个追上另一个啊!!!所以有 (x+m*t) - (y+n*t) = p * ll (p是周长,t是跳的次数,ll是a青蛙跳的圈数跟b青蛙的圈数之差。整个就是路程差等于纬度线周长的整数倍) 这里不懂就代个数看看!这也许就是传说中的抽象和建模吧!难点在这!
转化一下: (n-m)* t + p* ll= x – y; 实际就是求解不定方程a*x+b*y=c!拓展欧几里得!
令 a = n-m, b = ll, c = gcd(a, b), d =x-y;
有 a * t + b * p= d; (1)
要求的是t的最小整数解。
用扩展的欧几里德求出其中一组解t0,p0, 并令c = gcd(a, b);
有 a * t0 + b * p0 = c; (2)
因为c =gcd(a, b), 所以 a * t / c是整数,b * t / c 也是整数,所以 d / c 也需要是整数,否则无解。
(2)式两边都乘(d / c) 得 a * t0 *(d / c) + b* p0 * (d / c) = d;
所以t0 * (d / c)是最小的解,但有可能是负数。
因为a * ( t0 *(d / c) + b*n) + b * (p0 * (d / c) – a*n) = d; (n是自然数) 用到了上面的通式!!!
所以解为 (t0 * (d / c) % b + b) % b;
下面是几个拓展题目:
ZOJ 3609 :http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4712 求最小逆元
ZOJ 3593 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3593 求最小的步数,处理特殊一点就过去了
HDU 1576 http://acm.hdu.edu.cn/showproblem.php?pid=1576 做点处理即可
HDU 2669 http://acm.hdu.edu.cn/showproblem.php?pid=2669 裸的扩展欧几里得
下面给出引用的博客链接:
拓展欧几里得详解:http://m.blog.csdn.net/article/details?id=7786595
大神题解:http://blog.chinaunix.net/uid-22263887-id-1778922.html
#include#define ll long long using namespace std; ll x, y; ll ex_gcd(int a, int b) { if(b == 0){ x = 1; y = 0; ///最终状态来临,我们给出x==1,y==0!回溯开始! return a; } ll ans; ans = ex_gcd(b, a%b); ///一直递归,直到a==gcd,b==0的最终情况 ll temp = x; x = y; y = temp - a/b*y; return ans; } int main( ) { ll m, n, L; while(cin>>x>>y>>m>>n>>L){ ll a = n-m; ll b = L; ll c = x-y; ll gcd = ex_gcd(a, b); if(c%gcd!=0) {cout<<"Impossible"<