最近做了一些拓展欧几里得的题目呢,嘛,从一开始的不会到现在有点感觉,总之把我的经验拿出来和大家分享一下吧。
普通的欧几里得是用于解决求两个数a,b的gcd的,但是我们知道,gcd是线性组合 { ax+by | x,y∈Z }里的最小正元素(什么?不知道怎么来的?好吧。。。算法导论里数论算法那一章有证明),假若我们能够把这个x和y找出来,那么可以用来解决很多问题。
(以下的gcd和lcm均指(gcd(a,b)和lcm(a,b))
首先,假设ax+by=gcd这一个方程有一个特解x*,y*。那么显然,我们可以构造出这个方程的通解x0=x*+k*lcm/a,y0=y*-k*lcm/b,k∈Z。拓展欧几里得就能够帮我们把x*和y*求出来,这是用途之一。
其次,假若我们面对的方程是ax+by=d的形式,解是整数的情况下,我们可以利用上面说的解来推导出这个方程的解。
①假若gcd 不能整除 d,那么此时这个方程无整数解
证明: 假设存在整数解 n ,m 使得 an+bm=d,那么此时左右两边同时除以gcd有
a/gcd*n+b/gcd*m=d/gcd 显然,左边的和依然是整数,右边是分数,因为gcd不能整除d ,矛盾
所以此方程无整数解,证毕。
②在gcd是d的一个因子的情况下,这个方程可以变成 a*(x*gcd/d) + b*(y*gcd/d) = gcd,于是就转化成为了第一个问题的求解。但此时在求解通解的时候需要注意,该方程的通解并不是原来的通解乘上一个d/gcd这么简单。我想有一些人会这么做
x*gcd/d=x0,y*gcd/d=y0,因此,此时的通解就是 x = d*x0/gcd, y= y0*d/gcd
假若仅仅只是原来的通解乘上一个d/gcd,那么这只是现在方程的通解的一个子集而已,并不是通解。举个例子,假设d/gcd=2,那么你原来的k∈Z,现在乘上一个d/gcd之后,k的范围就变成了偶数,丢失了一部分的解。正确的是仅仅只使用原来的特解,也就是下面这一种情况。
x = d*(x*)/gcd + k*lcm/a , y = d*(y*)/gcd + k*lcm/b
只有这样的情况才是这个方程的通解,我做的很多题目都是需要对k的范围加以限定,然后选出合适的解
再者,可以用来求解一次同余式的问题,相信这个转化不是很难 。假设我们遇见的方程是这样的 ax≡ d(mod b),我们可以轻易的变成 ax + by = d的形式,也就是上面讨论过的那一个形式,不过在这里我们只需要一个x即可。
接下来讲解例题
POJ 1061 青蛙的约会
这一道题目就是上面说的第三种情况,求解一次同余式的问题,这道题目的一个式子就是 x + k*n ≡ y + k *m (mod L )通过移项后可以变成 k*(n-m)≡ y-x(mod L )这就是完完全全的第三种情况了,不过要保证k是正数,我们需要对k的范围作出限定,我们在求出特解之后在特解上进行加减的操作,保证k是恰好大于等于0的那一个解即可。这里的pair分别代表两只青蛙,pair的第一维是起始坐标,第二维是跳跃长度
#include
#include
#include
#include
using namespace std;
pairA,B,ans;
long long L,a,b,g,d,x,y;
long long gcd(long long a,long long b){return a%b==0?b:gcd(b,a%b);}
bool flag;
void egcd(long long a,long long b,long long& d,long long& x,long long& y);
int main(){
while(scanf("%lld%lld%lld%lld%lld",&A.first,&B.first,&A.second,&B.second,&L)!=EOF){
if(A.second0){
times=x/(L/d);
x-=times*(L/d);
}
printf("%lld\n",x);
}
return 0;
}
void egcd(long long a,long long b,long long& d,long long& x,long long& y){
(b==0)?(d=a,x=1,y=0):(egcd(b,a%b,d,y,x),y-=x*(a/b));
}
POJ 2142 C Looooops 和上面的青蛙的题差不多,也是一个求解同余式的问题,在这里就是判断 A+ x*C ≡ B (mod (1< UVA 10090 Marbles 这道题目就是显然的第二种情况了,不过我们需要保证求出来的两个解都是正数的情况下的最小值。按照我们上面的通解公式,求解两个不等式x>0和y>0,得到k的范围,然后写出一个有关代价Cost的算术函数,然后分析在k的定义域内取得极值的条件就可以了。其实把代价函数写出来之后发现是一个有关于k的一次函数,但是斜率是和两个物品的性价比有关系,因此只需要额外拿出来讨论一下即可,总之是在两个端点取得极值(函数自己写吧,这里就不给出了) UVA 10673 Play with Floor and Ceil 这道题真是太简单了。。。。既然任意解都可以的话。。通解是很明显的。。。因为floor(x)+1 == ceil(x) 在x不是整数情况下,答案很明显。。。 以上,拓展欧几里得例题讲解到此完毕,假若有函数不会写的也可以在下面留言。。其实我感觉只要把题目分析好了,就像个数学题。。重点是在草稿本上的推导。。#include
#include
POJ 2142 这道题目老实说我是分了三种情况来讨论的,一左一右有两种情况,还有一种就是两个都在左边的情况,方法和上面差不多,不过只是需要计算三种情况而已,一样的,在每种情况下分别解出通解x>0和y>0以及题目要求的(x+y)最小和(ax+by)最小,得出k的限定范围,然后在这三种情况里面取一个最优解即可。三种情况对应的方程分别是ax-by=d, by- ax =d和ax+by=d,注意一下通解的符号即可。(其实这里的第三种情况就是上面那一道题目。。。)这里的pa,pb,pc代表三种情况,pn代表答案
#include
#include