拓展欧几里得问题:
首先大家应该都知道,欧几里得算法吧也就是求最小公倍数gcd(a,b);而拓展欧几里得就是求解方程 ax+by=gcd( a,b )的所有整数解,既然这是拓展欧几里得所以核心和欧几里得算法类似。这一算法的核心就是利用递归求解x与y。
ok,既然是递归,那我们就应该知道
一:递归的原理(如何递归),
二:与递归需要维护的变量。
一,如何递归:
首先,如何递归就是指找到上一层与下一层的关系。我们知道欧几里得算法(以下用gcd(a,b)来表示),是利用递归思想找到了上一层与下一层之间的关系(还是附个板子吧)
void gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
(哈哈哈,上一层与下一层已经确定即gcd(a,b)与gcd(b,a%b))
而且gcd(a,b)=gcd(b,a%b)…(1)很明显是成立的,所以:
还记得我们的问题吗? ax+by=gcd(a,b),以及确定上一层与下一层的关系(x1,x2,y1,y2之间的关系),那该怎么办呢??(=@__@=)
(巨巨们呵呵一笑,换元Orz。)没错就是换元。那么就可以得到下面三个式子(为了更容易理解特意分开写):
一:ax1+by1=gcd(a,b);
二:a*x2+(a%b)*y2=gcd(b,a%b);
利用 (1) 式得:(这里再指明一下当前的问题,找到上一层x1,y1,与下一层x2,y2的关系)
三:ax1+by1=a*x2+(a%b)*y2;
(这里特意用x1,x2,y1,y2来表示就是为了表明他们是上下层的关系,即找他们之间的关系,不要想当然→_→)
然后众所周知a%b=a-(a/b)*b,然后换元得:
四:a *x1 + b * y1 = b * x2 + (a - (a / b) * b) * y2;
还是看不出来啊QAQ,别急,翻一下高数书
(先提取系数再待定系数法哈哈,啦啦啦(˘︶˘).。)
五:右式=b * (x2 - (a / b) * y2) + a * y2;
带入四得:
六:a * x1 + b * y1 = a * y2 + b * (x2 - (a / b) * y2)
最后一步(系数相同),得到两个式子:
七:x1=y2;
八:y1 = x2 - (a / b) * y2
(手撸一遍,很快的)
不过为什么要写到这里? 当然是想知道如何递归啊,仔细观察 (七) 与 (八) 式,你看到了什么(…(⊙_⊙;)…,神奇吧),我们其实已经实现了一层递归,他们的关系已经找到。(这个问题停一下)
什么时候停止呢??这里回想一下gcd(a,b);随着递归,b最终会变成 0,此时的方程因为b=0,就变成 a*x=gcd(a,b); (STOP!!!)然后得到一个解x=1, y=0。然后开始回溯,就可以得到a * x + b * y = gcd(a,b)的解。(一个特解,由它推出来通解)(amazing~~~);
如何求出所有通解呢?
a * x + b * y = gcd(a,b),我们让x增加b/gcd(a,b),让y减少a/gcd(a,b),等式两边相等(自己展开一下就知道啦)
我们就可以通过这种操作来更改x,y的值。
二:与递归需要维护的变量。
额~~~,很明显了以经,像gcd一样维护a,b因为需要求解,x,y所以要维护一下x,y;(看一下代码吧)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll ex_gcd(ll a,ll b, ll &x,ll &y)///文章讲的版本
{
if(a==0&&b==0)return -1;///无最大公约数
if(b==0){x=1;y=0;return a;}
ll g=ex_gcd(b,a%b,x,y),tmp=x;
x=y;y=tmp-a/b*y;
return g;
}
ll ex_gcd_kuangbin(ll a,ll b, ll &x,ll &y)///这种是kuangbin大佬的模板,他将x,y递归时换一下位置,然后再写一个式子即可(巨巨不愧是巨巨)。(本蒟蒻没看太懂,希望大佬指点)
{
if(a==0&&b==0)return -1;///无最大公约数
if(b==0){x=1;y=0;return a;}
ll g=ex_gcd(b,a%b,y,x);///差别
y-=a/b*x;
return g;
}
int main()
{
ll a,b,x,y;
cin>>a>>b;
// int tem=lcd_gcd(a,b,x,y);
int tem=ex_gcd2(a,b,x,y);
printf("%d%d\n",x,y);
}
这里强调一下函数是调用x,y的地址,因为x,y最开始没有赋值!!!。
问题一般化:
上面是求a * x + b * y = gcd(a,b)的解,而更一般的是a * x + b * y = c;(c是一个常数),怎么求呢??
两种情况:
(1) 如果c % gcd(a,b) != 0,无解。
(2) 如果c % gcd(a,b) ==0,且c / gcd(a,b) = k,那么将求出的解 x,y 都乘上k,就是答案,即kx,ky, (细心地读者会看到原式可以转化为a * x + b * y = k * gcd(a,b) );
(巨巨:用膝盖都能想出来,Orz);
例题:
题解:
需要先将原式处理一下变为1+by=ax;----->ax-by=1
由于本题是为了求解x所以可以直接转化为ax+by=1;(不影响)
然后正常求解就行(记得要乘以1/gcd(a,b),不懂的话好好把 问题一般化 读一下)
AC代码:
#include<bits/stdc++.h>
using namespace std;
int a,b,x,y;
int ex_gcd(int a,int b,int &x,int &y)
{
if(a==0&&b==0) return -1;
if(b==0){x=1;y=0;return a;}
int g=ex_gcd(b,a%b,x,y),tmp=x;
x=y;y=tmp-a/b*y;
return g;
}
void sol(int gcd,int b)
{
int k=1/gcd,tmp=b/gcd;
while(x>0)
{
x-=tmp;
}
x+=tmp;///因为题目要求最小非负整数,所以也没必要把 k 换成double。
x*=k;
cout<<x<<endl;
}
int main()
{
scanf("%d%d",&a,&b);
int gcd=ex_gcd(a,b,x,y);
sol(gcd,b);
}
总结:
第一次写这么长的文章(只算字),虽说挺耗时间,但是比只看博客理解的更深刻。也挺好,希望能够给更多人带来帮助(顺便涨点赞吧)。
告诫:
永远不要骗自己;