青蛙的約會
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 100498 Accepted: 19304
Description
兩 只青蛙在網上相識了,它們聊得很開心,於是覺得很有必要見一面。它們很高興地發現它們住在同一條緯度線上,於是它們約定各自朝西跳,直到碰面爲止。可是它 們出發之前忘記了一件很重要的事情,既沒有問清楚對方的特徵,也沒有約定見面的具體位置。不過青蛙們都是很樂觀的,它們覺得只要一直朝着某個方向跳下去, 總能碰到對方的。但是除非這兩隻青蛙在同一時間跳到同一點上,不然是永遠都不可能碰面的。爲了幫助這兩隻樂觀的青蛙,你被要求寫一個程序來判斷這兩隻青蛙 是否能夠碰面,會在什麼時候碰面。
我們把這兩隻青蛙分別叫做青蛙A和青蛙B,並且規定緯度線上東經0度處爲原點,由東往西爲正方向,單位長度1米,這樣我們就得到了一條首尾相接的 數軸。設青蛙A的出發點座標是x,青蛙B的出發點座標是y。青蛙A一次能跳m米,青蛙B一次能跳n米,兩隻青蛙跳一次所花費的時間相同。緯度線總長L米。 現在要你求出它們跳了幾次以後纔會碰面。
Input
輸入只包括一行5個整數x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。
Output
輸出碰面所需要的跳躍次數,如果永遠不可能碰面則輸出一行”Impossible”
Sample Input
1 2 3 4 5
Sample Output
4
Source
浙江
歐幾里得算法可以求出a,b的最大公約數。gcd(a,b)=gcd(b,a%b),公式兩邊b都靠近等號一側,因此很好記。
int gcd(int a,int b){return 0==b?a:gcd(b,a%b);}
爲什麼歐幾里得算法是對的,記住一個式子:a=bq+r(假設a>b。若a小於b,gcd(a,b)=gcd(b,a)。因此a大於還是小於b無所謂)。因爲a%m==0且b%m==0,所以必有r%m==0:a和b的約數必是r(即a%b)的約數。同時若r%n==0,b%n==0,必有a%n==0,因此b和r的約數必是a的約數。因此a、b約數中最大值必是b、r約數中的最大值。
**
擴展歐幾里得算法exGcd(a,b,x,y):求a,b的最大公約數同時可以求得方程組ax+by=gcd的一組特殊解(x,y)。
**從式子ax+by=gcd可以看出必須先求出gcd,才能求出解。而遞歸中的卻如此。
1、假設已經找到gcd,那麼y=0,x=gcd,顯然是該方程的解。而且在每一層遞歸函數exGcd(a,b,x,y)裏都有一個解(x,y)。
2、在遞歸回溯過程中,假設在第n+1層exGcd(a(n+1),b(n+1),x,y)找到了(x0,y0)是方程的解。那麼有a(n+1)*x0+b(n+1)*y0=c(1式)。
3、根據遞歸過程,在第n層調用了exGcd(bn,an%bn,x,y)進入第n+1層。因此在第n+1層的解滿足a(n+1)=bn,b(n+1)=an%bn,即(1)式等價於:bn*x0+(an%bn)%y0=c(2式)。又因爲an%bn=an-bn*(an/bn)。所以(2式子)等價於bn*x0+(an-bn*(an/bn))*y0=bn(x0-(an/bn)*y0)+an*y0=c(3式)。
3、假設回溯到第n層時,解爲(x1,y1),an*x1+bn*y1=c(4式)。對比(3式)和(4式)發現x1=y0,y1=x0-y0*(an/bn)。所以第n層的解可以通過第n+1層的解遞推出來。在最深層有解x=gcd,y=0,因此層層回溯,每層都會有一個關於當前層exGcd(an,bn,x,y)的解(xn,yn)使得an*xn+bn*yn=gcd(an,bn)。因此回溯到第一層時就得到了關於第一層的解。
代碼如下:
long long exGcd(long long a, long long b, long long &x, long long &y){
if(b==0){
x=1;
y=0;
return a;
}
long long g=exGcd(b,a%b,x,y);
long long temp=x;
x=y;
y=temp-(a/b)*y;//注意此處不能寫成y*a/b!
return g;
}
擴展歐幾里得算法應用之一就是:求二元一次方程組的解(x,y), a*x+b*y=c。
注意,這裏右邊是c,不是gcd(a,b)。假設其中x是我們關心的,所以研究x的解的情況。計算步驟:
1、用擴展歐幾里得算法求解方程:a*x+b*y=gcd(a,b). 可以同時得到gcd以及 一組特解(x0,y0)
2、如果c%gcd!=0,那麼根據式子a*x+b*y=c可知其無整數解(因爲a,b都可以提一個公因數gcd,但是c不能提因子gcd,等號左右兩邊不等價)。
3、那麼a*x+b*y=c的x的特殊解x1等於a*x+b*y=gcd(a,b)的特殊解x0乘以c/gcd: x1=x0*c/gcd。4、這一步的構造很關鍵,因爲引入了整數t,t不同解不同,這就是爲什麼會有無數解。因爲x1*a+y1*b=x1*a+y1*b+(t*a*b/gcd-t*a*b/gcd)=a(x1+t*b/gcd)+b(y1-t*a/gcd)=c,所以方程a*x+b*y=c的x的通解就是x=x1+t*b/gcd=x0*c/gcd+t*b/gcd。
5、x有無數種情況,我們關心特殊情況,比如在實際問題中經常需要找大於0且最小的x的值。如何尋找?找x即找t,下面對t進行分析。 x=x0*c/gcd+t*b/gcd。這裏的x肯定大於0,減去t*b/gcd剩下的x0*c/gcd也大於0,所以直接對x=x0*c/gcd進行分析。不妨讓t=x/(b/gcd)得到x中有t個b/gcd,然後在x中減去這t個b/gcd:t=x/(b/gcd),x=x-t*(b/gcd)。此時得到的x可能小於等於0,因此要做判斷,x小於等於0時加上b/gcd即可。
首先,此題有兩個變量,跳的次數P和跳的圈數Q。需要求次數的最小值。當然聯想到拓展歐幾里得算法的應用:求解二元一次線性方程組。兩隻青蛙相遇的條件是:(x+mP) mod L =(y+nP) mod L=0,但是這樣寫不是方程式的形式,所以改成等價的方程的形式: (x+mP)-(y+nP) =Q L,等價於x-y+P(m-n)=QL,x-y=(n-m)P+QL。令c=x-y,a=n-m,b=L,則aP+bQ=c,這就是標準的二元一次方程組,兩個未知量求其中一個未知量(跳到次數P)的最小值。
#include <iostream>
#include <cstring>
#include<cstdio>
#include <algorithm>
using namespace std;
long long exGcd(long long a,long long b,long long &x,long long &y){
if(b==0){
x=1;
y=0;
return a;
}
long long d=exGcd(b,a%b,x,y);
long long tmpX=x;
x=y;
y=tmpX-y*(a/b);//不能寫成y*a/b
return d;
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
long long ansX,x,y,n,m,L,x0,y0,a,b,c,gcd;
while(scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L)==5){
c=x-y;
a=n-m;
b=L;
gcd=exGcd(a,b,x0,y0);
if(c%gcd!=0) cout<<"Impossible"<<endl;
else{
ansX=x0*(c/gcd);
long long t=ansX/(b/gcd);
ansX=ansX-t*(b/gcd);
if(ansX<0) ansX+=b/gcd;
cout<<ansX<<endl;
}
}
return 0;
}
歡迎留言,積極討論,一起進步!