poj 1061 青蛙的约会 (扩展欧几里得模板)
Description
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。
Input
Output
Sample Input
1 2 3 4 5
Sample Output
4
首先介绍下什么是扩展欧几里得
扩展欧几里德算法
基本算法:对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)=ax+by。
证明:设 a>b。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,ab!=0 时
设 ax1+by1=gcd(a,b);
bx2+(a mod b)y2=gcd(b,a mod b);
根据朴素的欧几里德原理有 gcd(a,b)=gcd(b,a mod b);
则:ax1+by1=bx2+(a mod b)y2;
即:ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2;
根据恒等定理得:x1=y2; y1=x2-(a/b)*y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。
题意:公青蛙一开始在x位置,母青蛙在y位置。公青蛙每次跳m米,母青蛙每次跳n米,并且都是向右跳的。地球经线长度是L,然后地球是圆的,也就是说,跳到L、L+1、L+2……其实就是跳到0、1、2。 公青蛙想追母青蛙,问多少次后它们能跳到一起。如果它们永远不能相遇,就输出Impossible。
题解:就是求一个k,使x + k*m ≡ y + k*n (mod L) ,然后对方程化简,就变成(n-m) * k ≡ x-y (mod L)。然后这个方程其实就等价于(n-m)*k + L*s = x-y。这就是ax + by = c求整数x的模型。
要求ax + by = c的整数x解。(n-m)*t+L*S=x-y。首先,设d = gcd(a, b),方程两边除以d得到a/d * x + b/d * y = c/d,a是整除d的,b也是整除d的,而x、y都是整数解,所以要求c/d也是整数。如果c不整除d,当然就是Impossible。我们能求出ax0+by0=d的解x0和y0,那么两边乘以c/d即a(c/d * x0) + b(c/d * y0) = c,就可以得到原来方程的解x = (c/d * x0),y = (c/d * y0)。
所以x0 * (c / d)是最小的解,但有可能是负数。
因为a * ( x0 *(c / d) + b*n) + b * (y0 * (c / d ) – a*n) = c; (n是自然数)
所以解为 (x0 * (c / d) % b + b) % b;
#include <iostream> using namespace std; typedef long long ll; void gcd(ll a,ll b,ll &d,ll &x,ll &y) { if(!b) {d=a;x=1;y=0;} else {gcd(b,a%b,d,y,x);y-=x*(a/b);} } int main() { ll x,y,m,n,L,X,Y,d,r; while(cin>>x>>y>>m>>n>>L) { //求x ll a=m-n,b=-L,c=y-x; //要求ax+by=c; 最开始先构造 aX+bX=gcd(a,b)=d; (a/d)*X+(b/d)*X=1; // a*(c/d)*X+b*(c/d)*X=c; // 即 x=(c/d)*X; 注意x不一定是最小正整数解 // 用公式a(x+b*n)+b(y-a*n)=c;对x进行优化 // 那么这里的b是什么呢 gcd(a,b,d,X,Y); if(c%d) //这里要注意问题的实际意义 ab能除开d是一定的也是没用的 c必须能除开d才可以 //c要是除不开d就不可能相遇 能除开就把abc都除d cout<<"Impossible"<<endl; else { a=a/d; b=b/d; //关于为什么要将b除以d //这里要深刻理解最大公约数的意义 //假设gcd(15,9)=3; a=5,b=3,a变化b次可以遍历所有情况 //同理b变化a次可以遍历所有情况 求ax的x 把x对b取余即可遍历所有情况。 //这个数据的a的情况无非 5 10 15 三种。即b=3。 //如果不用新的b 就会多循环 b-b/gcd(a,b)次 不是最小的解 //比如 15 9 就会多遍历6次 // 9x+24y=3; x=-5,y=2; b=24的时候x优化成x=19 y=2-9=-7 // 3x+8y=1; b=8的时候x优化成x=3 y优化成 y=2-3=-1 //也就是说b=24不能遍历所有情况 可能会跳过最优解 //比如100x+200y=500; 答案显然是x取任意奇数 最小正整数解x=1; 而y=(5-x)/2; //如果x取-1 算结果的时候用b=200取余 x=199 很明显不是最小正整数解 //是错误的 即不能遍历所有奇数情况 应该化为x+2y=5; b=2; x=1;为正解。 //实际上这里的新的ab 已经不是原来的意义了 //原a=a0 b=b0 新a=a1 b=b1 //a0*b1=b0*a1 遍历所有情况 a1 b1实际上是查数用的 c=c/d; cout<<(X*c%b+b)%b<<endl; } } return 0; }