(一)欧几里得算法又称辗转相除法,是求解两个数的最大公约数的算法,基本定义为:
设 a=qb+r,其中a,b,q,r都是整数,则:gcd(a,b)= gcd(b,r)
利用递归实现该算法:
long long gcd(int a,int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
辗转相除法的应用:(水题)
nefu 116:两仪剑法 http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=116
题目分析:
由题意知:该题是要求M和N的最小公倍数,由于数据较大,正常用会导致数据溢出而WA,所以要利用该求最小公倍数的公式变形,先去除,再去乘,来进行数据范围的控制,公式为:
则代码实现如下:
#include
#include
using namespace std;
long long gcd(int a,int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
int main()
{
int m,n;
while(scanf("%d%d",&m,&n)!=EOF)
{
printf("%lld\n",(m/gcd(m,n))*n);
}
return 0;
}
设a和b不全为0,则存在整数x和y,使得 gcd(a,b)= xa + yb
拉梅定理:用欧几里得算法计算两个正整数的最大公因子时,所需的除法次数不会超过两个整数中较小的那个十进制数的倍数的5倍。
拉梅定理推论:求两个正整数a,b,a>b 的最大公因子需要O(log2a)3次的位运算。
由扩展欧几里得的推论:如果gcd(a,b)=1,则称a与b互素。
整数a和b互素的充分必要条件:存在整数x和y,使得xa+yb=1 。
扩展欧几里得算法实现:
typedef long long int64;
using namespace std;
int64 exgcd(int64 m,int64 & x,int64 n,int64 & y) //Extend Euclid
{
int64 x1,y1,x0,y0;
x0 = 1;y0 = 0;
x1 = 0;y1 = 1;
int64 r=(m%n+n)%n;
int64 q=(m-r)/n;
x=0;y=1;
while(r)
{
x=x0-q*x1;
y=y0-q*y1;
x0=x1;
y0=y1;
x1=x;
y1=y;
m=n;
n=r;
r=m%n;
q=(m-r)/n;
}
return n; //返回值为m和n的最大公约数,修改后的x和y的值是所求值
}
扩展欧几里得算法应用:
POJ 1061:青蛙的约会 http://poj.org/problem?id=1061
题目分析:设两只青蛙跳了t步后相遇,A蛙的坐标为:x+mt ,B蛙的坐标为:y+nt 。两蛙相遇的条件为: ,公式变形为: 设 ,求满足 的最小t(t>0),即求一次同余方程 的最小正整数解。具体求解过程为3步:
(1)写出方程,用扩展欧几里得函数求解,即 此时X是一个解,但不是最后的解。
(2)若(x-y)%gcd(n-m,L)==0, 则有解(指x和y都有解)。
(3)有解后,设M=gcd(n-m,L),X=X(x-y)/M
然后:(X%(L/M)+(L/M))%(L/M) 就是最后的解,即为本题的t的值。
代码实现如下:
#include
#include
typedef long long int64;
using namespace std;
int64 exgcd(int64 m,int64 & x,int64 n,int64 & y) //Extend Euclid
{
int64 x1,y1,x0,y0;
x0 = 1;y0 = 0;
x1 = 0;y1 = 1;
int64 r=(m%n+n)%n;
int64 q=(m-r)/n;
x=0;y=1;
while(r)
{
x=x0-q*x1;
y=y0-q*y1;
x0=x1;
y0=y1;
x1=x;
y1=y;
m=n;
n=r;
r=m%n;
q=(m-r)/n;
}
return n; //返回值为m和n的最大公约数,修改后的x和y的值是所求值
}
int main()
{
int yy;
int64 r,t;
int64 x,y,m,n,l;
int64 ar,br;
cin>>x>>y>>m>>n>>l;
int64 M=exgcd(n-m,ar,l,br);
if((x-y)%M||m==n)
cout<<"Impossible"<