再探扩展欧几里得算法的应用

今天又复习了一下扩展欧几里得算法,并且了解了一些扩展欧几里得算法可以解决的新东西。。。

对欧几里得算法一点都不会的请移驾本空间另一篇文章:传送门

下面以三个题为例总结相关知识:

POJ2142 The Balance

这道题要求用扩展欧几里得算法求解a*x+b*y=c的一组解,要求此解满足|x|+|y|最小,|x|+|y|相同时a*|x|+b*|y|最小。

不妨设a>b,若a<b,交换a、b即可。

首先令d=gcd(a,b),用扩展欧几里得求解a*x+b*y=d,然后令x=x*(c/d),y=y*(c/d),就求出了方程的任意一组解(x,y)。

根据相关定理,方程的全部解可以表示为:x+i*(b/d),y-i*(a/d),其中i为常数。观察函数:|x+i*(b/d)|+|y-i*(a/d)|,当y-i*(a/d)在0附近时,函数取最小值,证明略。因此我们只需要枚举y-i*(a/d)在0附近的两个整数解,二者中选更优的即可。

还有一种讲起来更通俗的方法:|x|+|y|取最小值,要么x为方程的最小正整数解,要么y为方程的最小正整数解。不过此法我没有写程序提交。

#include<iostream>

using namespace std;

int a,b,c,d,x,y,t1,t2,x1,y1,x2,y2,flag;

 

int abso(int x)

{

return x>0?x:-x;

}

 

int exgcd(int a,int b,int &x,int &y)

{

if(!b) {x=1; y=0; return a;}

int c=exgcd(b,a%b,x,y),z=x;

x=y; y=z-y*(a/b);

return c;

}

 

int main()

{

while(cin>>a>>b>>c&&a)

{

if(a<b) {swap(a,b); flag=1;} else flag=0;

d=exgcd(a,b,x,y);

x*=c/d; y*=c/d;

t1=y*d/a;

if(y-a/d*t1<0) t1--;

t2=t1+1;

x1=abso(x+b/d*t1); y1=abso(y-a/d*t1);

x2=abso(x+b/d*t2); y2=abso(y-a/d*t2);

if(x1+y1<x2+y2||(x1+y1==x2+y2&&a*x1+b*y1<a*x2+b*y2)) {x=x1; y=y1;}

else {x=x2; y=y2;}

if(flag) cout<<y<<' '<<x<<endl; else cout<<x<<' '<<y<<endl;

}

return 0;

}

 

POJ2115 C Looooops 此题就是求一次同余方程c*a mod p=b的最小正整数解a。令d=gcd(c,p),若d|b,方程有解,并且所有的解关于p/d同余;否则无解。做法是用扩展欧几里得算法求方程c*x+p*y=d的一组解,则原方程的一组解为x*(b/d)。根据解关于p/d同余的性质可以取模求出最小正整数解。

#include<iostream>

using namespace std;

typedef long long LL;

LL f[35],a,b,c,k,p,d,i,x,y;

LL exgcd(LL a,LL b,LL &x,LL &y)

{

if(!b) {x=1; y=0; return a;}

LL c=exgcd(b,a%b,x,y),z=x;

x=y; y=z-y*(a/b);

return c;

}

int main()

{

for(i=1,f[0]=1;i<=32;i++) f[i]=f[i-1]*2;

while(cin>>a>>b>>c>>k&&k)

{

p=f[k];

b=(b-a+p)%p;

d=exgcd(c,p,x,y);

if(b%d==0) cout<<(x*b/d%p+p)%(p/d)<<endl; else cout<<"FOREVER\n";

}

return 0;

}


POJ2891 Strange Way to Express Integers 求最小正整数T,满足T mod ai=bi。这道题看第一眼:裸的中国剩余定理。再一看,没保证所有ai互质。。。先说说如果ai互质,用中国剩余定理的做法:

记所有ai的最小公倍数为mul。对于每个ai,求出一个ci,使得ci能被数列a中除了ai以外的所有数整除,并且ci mod ai=1。

ci可以这样求:首先用扩展欧几里得定理求出u*x+v*y=1的任意一组解(x,y),其中u=mul div ai,v=ai,则u*x就是ci。可以这样理解:ci一定是u的倍数,因为它能被数列中除了ai的所有数整除;不妨设ci是u的x倍。由于ci mod ai=1,也就是说ci-1能被ai整除,所以ci-1是v的倍数,不妨设ci-1是v的y倍,所以ci-1=v*y,也就是u*x+v*y=1。

求出ci后,令di=ci*bi。把所有di相加就得到了一个满足要求的T值。由于让t最小,只需要mod mul即可,但是要注意mod出来负数时要处理一下。

但是这题并没有保证所有ai互质,因此我们使用扩展欧几里得算法求解。

对于第i个方程T mod ai=bi,假设之前求得的解为T,之前所有ai的最小公倍数为lcm,那么需要让T加上一个值 k*lcm,其中k为常数。并且使得:(T+k*lcm)mod ai=bi。这个方程就是上面刚讲过的一次线性同余方程了。若该方程无解,那么T不存在。

#include<iostream>

#include<cstdio>

using namespace std;

typedef long long LL;

int n,i,fail;

LL a,b,lcm,now,k,d,x,y;

 

LL exgcd(LL a,LL b,LL &x,LL &y)

{

if(!b) {x=1; y=0; return a;}

LL c=exgcd(b,a%b,x,y),z=x;

x=y; y=z-y*(a/b);

return c;

}

 

int main()

{

while(cin>>n)

{

scanf("%lld%lld",&a,&b);

lcm=a; now=b; fail=0;

for(i=1;i<n;i++)

{

scanf("%lld%lld",&a,&b);

b=(b-now%a+a)%a;

d=exgcd(lcm,a,x,y);

if(b%d==0) k=x*(b/d)%a; else fail=1;

now+=k*lcm;

lcm=lcm/d*a;

now=(now%lcm+lcm)%lcm;

}

if(fail) cout<<"-1\n"; else cout<<now<<endl;

}

return 0;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值