POJ 青蛙的約會 (擴展歐幾里得)


青蛙的約會

Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 20000/10000K (Java/Other)
Total Submission(s) : 2   Accepted Submission(s) : 2
Problem 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
PKU
 
 
 
 

首先我們先討論歐幾里得算法 ( gcd ):

gcd( a, b )即求兩個數的最大公約數

遞歸算法:

int gcd( int a, int b ) {   return b==0?a:gcd( b, a%b );  //gcd(a,b) = gcd(a%b,b),這個遞歸一次以后就終止了無法保證a b可以繼續減小,所以把 b 和 a%b交換順序。 }

非遞歸算法:

int gcd( int a, int b ) {   if( b==0 )return 0;   while(b)   {     int t=a%b;     a=b;     b=t;   }   return a;

}

現在我們討論算法的正確性,即證明gcd(a,b)==gcd(b,a%b),我們只要證明gcd(a,b)==gcd(a-b,b)即可,因為可以由此逐步擴展為gcd(a,b) == gcd(a-k*b,b),而 gcd(a-k*b,b)==gcd(a%b,b)。 因為a,b的公約數必然是a-b,b的公約數故 gcd(a,b) <= gcd(a-b,b);另a-b b的公約數也必然是a b的公約數,gcd(a,b) >= gcd(a-b,b).所以gcd(a,b) == gcd(a-b,b)。

再說擴展歐幾里得:

擴展歐幾里德算法是用來求解a*x+b*y==gcd(a,b)這樣的方程的。同樣利用gcd(a,b)==gcd(b,a%b)把a*x+b*y==gcd( a, b )轉化為b*x'+(a%b)*y'==gcd( b, a%b );

根據遞歸的思想,假設現在我們已經求出了x' y',剩下的關鍵就是如何用x' y'求出x y.我們觀察gcd(b,a%b) = b*x'+(a%b)*y',只要把右邊重新寫成 a*x+b*y 的形式就行了,所以需要對b*x'+(a%b)*y'進行變形,因為a%b == a-a/b*b,故b*x'+(a%b)y' = b*x'+(a-a/b*b)y' == a*y' + b*(x'-a/b*y') .

這樣便可得出 x = y' y = x'-a/b*y'。

所以擴展gcd的遞歸算法為

LL exgcd( LL a, LL b, LL &x, LL &y ) {   LL d, t;   if( b==0 )   {     x=1, y=0;     return a;    }   d=exgcd( b, a%b, x, y );   t=x, x=y, y=t-a/b*y;   return d;        // 返回gcd( a, b ); }

 這樣我們就得到了方程的解 :

x==x0+b*t;    //    特解+通解

y==y0+a*t;

然后再看一般形式 a*x+b*y==c;

當且僅當 c%gcd( a,b )==0時方程才有解。

a*x+b*y==c的求解可以先求出a*x+b*y=gcd(a,b),然后將x y擴大c/gcd(a,b)倍就可以了。

 

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

long long Gcd(long long a,long long b){
    return b==0?a:Gcd(b,a%b);
}

void exGcd(long long a,long long b,long long &x,long long &y){
    if(b==0){
        x=1; y=0;
        return ;
    }
    exGcd(b,a%b,x,y);
    long long tmp=x;
    x=y;
    y=tmp-a/b*y;
}

int main(){

    //freopen("input.txt","r",stdin);

    long long x,y,m,n,L;
    long long a,c,k1,k2,r;
    while(~scanf("%I64d%I64d%I64d%I64d%I64d",&x,&y,&m,&n,&L)){
        a=n-m; c=x-y;
        r=Gcd(a,L);
        if(c%r){
            puts("Impossible");
            continue;
        }
        a/=r; L/=r; c/=r;
        exGcd(a,L,k1,k2);
        long long ans=c*k1-c*k1/L*L;
        if(ans<0)
            ans+=L;
        printf("%I64d\n",ans);
    }
    return 0;
}

 

 

 

#include <stdio.h>
 typedef long long LL;
 LL exgcd( LL a, LL b, LL &x, LL &y )
 {
     LL d, t;
     if( b==0 )
     {
         x=1, y=0;
         return a;    
     }
     d=exgcd( b, a%b, x, y );
     t=x, x=y, y=t-a/b*y;
     return d;
 }
 
 // (a+c*m)%2^k=b ==> c*m-n*2^k=b-a;
 int main( )  
 {  
     LL A,B,C,k, a, b, c, x, y, n;  
     while(scanf("%lld %lld %Illd %lld",&A,&B,&C,&k))  
     {  
         if(!A && !B && !C && !k)  
             break;  
   
         a=C, b=B-A, n=(LL)1<<k;  //2^k   
         LL d=exgcd(a,n,x,y);  //求a,n的最大公約數d=gcd(a,n)和方程d=ax+by的系數x、y  
         if(b%d!=0)  //方程 ax=b(mod n) 無解  
            puts("FOREVER"); 
         else  
         {  
             x=(x*(b/d))%n;  //方程ax=b(mod n)的最小解  
             x=(x%(n/d)+n/d)%(n/d);  //方程ax=b(mod n)的最整數小解  
             printf("%lld\n",x);  
         }  
     }  
     return 0;  
 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM