1. 程式人生 > >【算法】歐幾裏得算法與青蛙約會oj

【算法】歐幾裏得算法與青蛙約會oj

題意 log lld 網上 math 求解 程序 重要 namespace

歐幾裏得和擴展歐幾裏得算法

題目:

  1. poj 1061
  2. poj 2142
  3. 雙六

擴展歐幾裏得算法詳解

先說歐幾裏得算法:歐幾裏得算法輾轉相除求\(gcd\)。求\(a、b\)\(gcd\),則利用的性質是:\(gcd(a,b)=gcd(b,a\%b)\),而\(gcd(a,0)=a\),這樣,輾轉除下去,當第二個參數為0,第一個參數就是最大公約數。

int gcd(int a,int b){
    while(b!=0){
        int tmp = a%b;
        a = b;
        b = tmp;
    }
    return a;
}

擴展歐幾裏得算法:擴展歐幾裏得算法不僅可以用來求最大公約數,還可以求逆元/滿足ax+by=gcd(a,b)的x和y。基於的原理是:ax+by=gcd(a,b)一定存在解(x,y)。

一個用處就是:問ax+by=1是否有解,就是看a,b是否互質,即gcd(a,b)=1。

求滿足ax+by=gcd(a,b)的(x,y)的過程,就是證明ax+by=gcd(a,b)一定有解的過程。

\(ax+by=gcd(a,b)=gcd(b,a\%b)=bx_2+a\%by_2\)

\(a\%b=a-a/b*b\)

因此 \(ax+by =bx_2+(a-a/b*b)y_2 =ay_2+b(x_2-a/b*y2)\)

從而 \(x = y2;y=x_2-a/by_2\)

層層遞歸下去,最終當\(b=0\)時,返回\(gcd(a,b)=a\),此時有一組解\(x=1,y=0\),回溯,利用上式求出\(x,y\)

從而,這就建立了要求的(x,y)和前一狀態的關系,算法的遞歸實現如下:

int exgcd(int a,int b,int &x,int &y){
    /*輸入ax+by=gcd(a,b)的a,b,返回gcd(a,b),同時求出滿足此式的解(x,y)*/
    if(b==0){
        x = 1;
        y = 0;
        return a;
    }
    int p = exgcd(b,a%b,x,y);
    int tmp = x;
    x = y;
    y = tmp - a/b*y;
    return p;
}
int main(){
    int x,y;
    int gcd = exgcd(24,18,x,y);
    printf("24*%d + 18*%d = %d\n",x,y,gcd);
    return 0;
}

繼續.....

僅僅知道歐幾裏得解\(ax+by=gcd(a,b)\)還不夠,擴展歐幾裏得最好用的地方在於其求解乘法逆元。所謂逆元:若\(ax==1mod(m)\),則稱\(x\)\(a\)關於\(m\)的逆元,逆元可能有許多個,求的是其中最小的一個。

\(ax==1mod(m)\) \(=>\) \(ax+my=1\)有解,求此式的解中最小的\(x\)即可。即求出此式\(x\)\(x=x\%m\),如果\(x<0\)\(x+=m\)

求解代碼借助exgcd:

/*求逆元模板*/
int exgcd(int a,int b,int &x,int &y){ 
    if(b == 0){ 
        x = 1; 
        y = 0; 
        return a; 
    } 
    int p =exgcd(b,a%b,x,y); 
    int temp = x; 
    x = y; 
    y = temp - (a/b) *y; 
    return p; 
} 
//求逆元 求a關於m的最小逆元 
int cal(int a,int m){ 
    int x,y; 
    int gcd = exgcd(a,m,x,y); 
    if(1%gcd != 0) return -1; 
    x *= 1/gcd; 
    int ans = x % m; 
    if(ans<=0) ans+=m;  //保證正的且最小
    return ans; 
}

/*另一個簡潔版本的求逆元*/
void exgcd(int a,int b,int &x,int &y){
    if(b == 0){
        x = 1;
        y = 0;
    }else{
        exgcd(b,a%b,y,x);
        y = y - (a/b)*x; //此處應註意
    }
}

例題:青蛙約會poj 1061

代碼解釋:https://blog.csdn.net/liangdong2014/article/details/38732745

poj 1061 青蛙約會
兩只青蛙在網上相識了,它們聊得很開心,於是覺得很有必要見一面。它們很高興地發現它們住在同一條緯度線上,於是它們約定各自朝西跳,直到碰面為止。可是它們出發之前忘記了一件很重要的事情,既沒有問清楚對方的特征,也沒有約定見面的具體位置。不過青蛙們都是很樂觀的,它們覺得只要一直朝著某個方向跳下去,總能碰到對方的。但是除非這兩只青蛙在同一時間跳到同一點上,不然是永遠都不可能碰面的。為了幫助這兩只樂觀的青蛙,你被要求寫一個程序來判斷這兩只青蛙是否能夠碰面,會在什麽時候碰面。? 我們把這兩只青蛙分別叫做青蛙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

思路:

在一個周長是\(L\)的環上,A青蛙處於\(x\)處,每次跳\(m\),B青蛙處於\(y\)處,每次跳\(n\),跳相同的步數後,兩青蛙處於同一位置。由此,設跳\(t\)步相遇,則:

\((x+m*t)\%L=(y+n*t)\%L\)

\(=>(m-n)*t\%L=(y-x)\%L\)

\(=>(m-n)*t\%L=y-x\)

若此式有解,則應有,

\(=>(m-n)*t_1+L*t_2=y-x\)有解,由exgcd知,有解的條件是

\(=>(y-x) \% gcd(m-n,L) =0\)

若有解,下面求解滿足條件的最小\(t_1\),進而推出\(t\)

由exgcd求出滿足\((m-n)*t_1+L*t_2=gcd(m-n,L)\)的一組解:

exgcd(m-n,L,t1,t2);

由於\(y-x\)\(gcd(m-n,L)\)的整數倍,則\((m-n)*t_1+L*t_2=y-x\)的一個解是:

\(=>t_1=t_1*(y-x)/gcd(m-n,L)\);

註意這樣求出來的\(t_1\)可能並不是最小,因此對\(t_1\)做如下操作:

\(=>t_1=t_1\%L\)變為最小;

\(t_1\)還可能是負值,因此,變為正的:

\(=>if_{ t_1<0}:t_1=t_1+L;\)

從而解出了\(t=t_1\)

實現代碼:

#include<iostream>
using namespace std;
typedef long long ll; 
ll x,y,m,n,L;
ll gcd(ll a,ll b){
    while(b!=0){
        ll tmp = a%b;
        a = b;
        b = tmp;
    }
    return a;
}
void exgcd(ll a,ll b,ll&x,ll &y){
    if(b == 0){
        x = 1;
        y = 0;
    }else{
        exgcd(b,a%b,y,x);
        y = y - (a/b)*x;
    }
}

int main(){
    ll a,b,c,d,t1,t2,L2;
    while(scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L)!=EOF){
        a = m-n;
        b = y-x;
        d = gcd(a,L);
        if(b % d != 0){
            printf("Impossible\n");
        }else{
            exgcd(a,L,t1,t2);
            t1 = t1*b/d; //先變換到求at1+Lt2=y-x,再求最小,不能先求最小再變換,否則題意不符 
            t1 = t1 % L;
            if(t1<0){
                t1 += L;
            }
            printf("%lld\n",t1);
        }
    }
    return 0;
}

【算法】歐幾裏得算法與青蛙約會oj