先说欧几里德算法
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
//辗转相除,如果b为0,返回a;
}
扩展欧几里德
先贴代码
int exgcd(int a,int b,int &x,int &y)
{
int d=a;
if(b==0)
{
x=1,y=0;
}
else
{
d=exgcd(b, a%b, y, x);
y-=(a/b)*x;
}
return d;
}
欧几里德算法递归最终的状态是: a= gcd , b = 0,这时,我们就会有: 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
于是你就很“容易“写出扩展欧几里德的递归算法,如上;
-
-
-
很多题目不是直接让你求ax+by=gcd,而是变相的问你是否存在一组解,使得ax+by=n
这里的n如果n%gcd(a,b)==0,就有解,否则一定无解。
但是有时候题目会包含隐含条件,要求x和y都要大于等于0,这个时候就不能直接判断了
我们知道,得到的x和y其实只是gcd的一组解,可以通过这组通解得到通解,假设得到特解x0,y0,则通解可表示为:
x = x0 + (b/gcd)*t
y = y0 – (a/gcd)*t
如果不清楚为什么来的,可以带入原方程,你会发现gcd并没有改变
得到通解后,通过
x*=n/gcd(a,b);
x=(x%(b/gcd)+b/gcd)%(b/gcd);
把x变成符合题意的正整数,再算出y,判断x和y是否都不为负数即可
贴一个代码:
#include<stdio.h>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
int d=a;
if(b==0)
{
x=1,y=0;
}
else
{
d=exgcd(b, a%b, y, x);
y-=(a/b)*x;
}
return d;
}
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
int a,b,n;
while(cin>>a>>b>>n)
{
int x,y;
int gc=exgcd(a, b, x,y);
if(n%gc!=0){printf("NO\n");continue;}
x=x*n/gc;
x=(x%(b/gc)+b/gc)%(b/gc);//把x变成正数(因为用exgcd可能求出负值)
y=(n-x*a)/b;
if(x>=0&&y>=0)
printf("YES\n");
else printf("NO\n");
}
return 0;
}