扩展欧几里得算法+板子+例题

目录

欧几里得算法

扩展欧几里得算法

例题


欧几里得算法:

  有两个数a,b,求这两个数的最大公约数

  欧几里得的一个定理:gcd(a,b)=gcd(b,a%b)

  这样,就可以在近乎log的时间复杂度里求解a和b的最大公约数

  这就是欧几里得算法

  代码:

int gcd(int x,int y)
{
    return y==0?x:gcd(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

因为欧几里德算法递归停止的状态是: a= gcd,b = 0

那么,只要 a = gcd 的系数是 1 ,那么只要 b 的系数是 0 或者其他值

然后从该最终状态反推到最初状态

假设当前我们要处理的是求出 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 那么:

        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

    以上就是扩展欧几里德算法的全部过程,依然用递归写:

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;y=0;
        return a;
    }
    int ans=exgcd(b,a%b,x,y);
    int t=x;x=y;
    y=t-a/b*y;
    return ans;
}

欧几里德算法部分好像只能用来求解最大公约数,

但是扩展欧几里德算法就不同了,既可以求出最大公约数,还可以顺带求解出使得: a*x + b*y = gcd 的通解 x 和 y

比如:求解形如 a*x +b*y = c 的通解

给出计算模板:

int cal(int a,int b,int c)
{
    int x,y;
    int gcd=exgcd(a,b,x,y);
    if(c%gcd!=0)return -1;
    x*=c/gcd;
    if(b<0)b=-b;
    int ans=x%b;
    if(ans<=0)ans+=b;
    return ans;
}

还有最小整数解之类的问题,但都是大同小异

例题

OJ 3609

求最小逆元【裸的求逆元模板题,c=1】

#include
#include
#include
#include
#include
using namespace std;

int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1;y=0;
		return a;
	}
	int ans=exgcd(b,a%b,x,y);
	int tmp=x;x=y;
	y=tmp-a/b*y;
	return ans;
}
int cal(int a,int b)
{
	int x,y;
	int gcd=exgcd(a,b,x,y);
	if(1%gcd!=0)return -1;
	x*=1/gcd;
	if(b<0)b=-b;
	int ans=x%b;
	if(ans<=0)ans+=b;
	return ans;
}
int main()
{
	int t;scanf("%d",&t);
	while(t--)
	{
		int a,b;scanf("%d%d",&a,&b);
		int ans=cal(a,b);
		if(ans==-1)puts("Not Exist");
		else printf("%d\n",ans);
	}
}

ZOJ 3593

求最小的步数;这里求到最后就是求|x|+|y|的最小值

线性规划,画出图之后可知最下面的交点处x=y时值最小

但是x不等于y,所以枚举其附近的点

然后联立x及y的通解(在x=y的情况下)求出T

再分类讨论x  y 同向异向的问题

#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1;y=0;
        return a;
    }
    LL ans=exgcd(b,a%b,x,y);
    LL t=x;x=y;
    y=t-a/b*y;
    return ans;
}
LL cal(LL a,LL b,LL c)
{
    LL x,y;
    LL gcd=exgcd(a,b,x,y);
    if(c%gcd!=0)return -1;
    x*=c/gcd;
    y*=c/gcd;
    a/=gcd;
    b/=gcd;
    LL ans=LLONG_MAX;
    LL f;
    LL mid=(y-x)/(a+b);
    for(LL T=mid-1;T<=mid+1;T++)
    {
        if(abs(x+b*T)+abs(y-a*T)==abs(x+b*T+y-a*T))///即x,y同号,同方向走
            f=max(abs(x+b*T),abs(y-a*T));///可以走x步a,y步b,还可以走c//(a+b);所以选择步数最多的那种走法
        else
            f=fabs(x+(-(y-(a+b)*T)));///朝不同的方向走,走了x+y步  x+(-y)使同号
        ans=min(ans,f);
    }
    return ans;
}
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        LL A,B,a,b;
        scanf("%lld%lld%lld%lld",&A,&B,&a,&b);
        LL ans=cal(a,b,B-A);
        printf("%lld\n",ans);
    }
}

POJ 1061  青蛙的约会【裸的扩展欧几里得】

不过这里有一个小处理,就是一个静止另一个做相对运动

所以传参是m-n,L,y-x

#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;

LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1;y=0;
        return a;
    }
    LL ans=exgcd(b,a%b,x,y);
    LL t=x;x=y;
    y=t-a/b*y;
    return ans;
}
LL cal(LL a,LL b,LL c)
{
    LL x,y;
    LL gcd=exgcd(a,b,x,y);
    if(c%gcd!=0)return -1;
    x*=c/gcd;
    if(b<0)b=-b;
    LL ans=x%b;
    if(ans<=0)ans+=b;
    return ans;
}
int main()
{
    LL x,y,m,n,L;
    scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L);
    LL ans=cal(m-n,L,y-x);
    if(ans==-1)puts("Impossible");
    else printf("%lld\n",ans);
}

HDU 2669  

裸题,但是这里输出的是两个数,注意输出就好

#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;

LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1;y=0;
        return a;
    }
    LL ans=exgcd(b,a%b,x,y);
    LL t=x;x=y;
    y=t-a/b*y;
    return ans;
}
LL cal(LL a,LL b,LL c)
{
    LL x,y;
    LL gcd=exgcd(a,b,x,y);
    if(c%gcd!=0)return -1;
    x*=c/gcd;
    b/=gcd;
    if(b<0)b=-b;
    LL ans=x%b;
    if(ans<=0)ans+=b;
    return ans;
}
int main()
{
    LL a,b;
    while(~scanf("%lld%lld",&a,&b))
    {
        LL ans=cal(a,b,1);
        if(ans==-1)puts("sorry");
        else printf("%lld %lld\n",ans,(1-ans*a)/b);
    }
}

 

你可能感兴趣的:(笔记,算法&理论,板子,数论)