POJ 1061 青蛙的約會

1 擴展歐幾里德算法

擴展歐幾里德算法是用來在已知a, b求解一組x,y,使它們滿足貝祖等式: ax+by=gcd(a,b)=d (解一定存在,根據數論中的相關定理)。擴展歐幾里德常用在求解模線性方程及方程組中。
gcd: 最大公約數 greatest common divisor

關鍵點 gcd(a,b)=gcd(b,amodb)
假設a=kb+r , d 是 a, b的公約數
那麼 因爲r=kba 所以r|d , 因此 d也是(b,r)即(b,a mod b)的公約數。
反向證明同上。

// gcd 求解代碼
// ax + by = d
int x, y, d;

void gcd(int a, int b) {
    if (b == 0) { 
        x = 1; y = 0; d = a;
        return;
    }

    gcd(b, a%b);

    int temp = x;
    x = y;
    y = temp - a/b*y;
    return;
}

關於迭代的x,y變換

ax+by=d=xb+y(amodb)

a=kb+r

ax+by=ya+(xyk)b

一一對應,

x=y,y=xyk=xya/b

2 青蛙的約會

兩隻青蛙在網上相識了,它們聊得很開心,於是覺得很有必要見一面。它們很高興地發現它們住在同一條緯度線上,於是它們約定各自朝西跳,直到碰面爲止。可是它們出發之前忘記了一件很重要的事情,既沒有問清楚對方的特徵,也沒有約定見面的具體位置。不過青蛙們都是很樂觀的,它們覺得只要一直朝着某個方向跳下去,總能碰到對方的。但是除非這兩隻青蛙在同一時間跳到同一點上,不然是永遠都不可能碰面的。爲了幫助這兩隻樂觀的青蛙,你被要求寫一個程序來判斷這兩隻青蛙是否能夠碰面,會在什麼時候碰面。 (可見網聊是多麼不靠譜啊,可憐的小青蛙)
我們把這兩隻青蛙分別叫做青蛙A和青蛙B,並且規定緯度線上東經0度處爲原點,由東往西爲正方向,單位長度1米,這樣我們就得到了一條首尾相接的數軸。設青蛙A的出發點座標是x,青蛙B的出發點座標是y。青蛙A一次能跳m米,青蛙B一次能跳n米,兩隻青蛙跳一次所花費的時間相同。緯度線總長L米。現在要你求出它們跳了幾次以後纔會碰面。

輸入

輸入只包括一行5個整數x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

輸出

輸出碰面所需要的跳躍次數,如果永遠不可能碰面則輸出一行”Impossible”


思路

在兩隻青蛙碰見時會有

k(mn)+αL=yx

其中k 爲跳的次數 α 爲整數。

x=k,a=mn,y=α,b=L,D=yx 可得
ax+by=D
注意,此處D不一定是a,b的公約數

下面這段推導參考了聖劍的博客
不過沒關係,先用擴展歐幾里得得出ax+by=gcd(a,b) 的一個解x0,y0 ;再觀察兩個式子:

ax+by=D
ax0+by0=d (令d=gcd(a,b) )

將上面的式子兩邊都除以d ,得:
xa/d+yb/d=D/d
因爲d爲a,b的最大公約數,所以a/d,b/d爲整數,所以D/d必定爲整數,否則方程無解;
將下面式兩邊都乘以d/D與上式比較:

ax+by=D
ax0D/d+by0D/d=D

x=x0D/d,y=y0D/d


擴展解

對於方程 ax0/d+by0/d=1
因爲d是a,b的gcd, 所以a/d b/d 互質,那麼若有

u(b/d)(a/d)+v(a/d)(b/d)=0

(x0+u(b/d))(a/d)+(y0+v(a/d))(b/d)=1

故而方程 ax/d+by/d=1 的解爲

x=x0+u(b/d)

由於 x=x0D/d,y=y0D/d
則本題的解k=x=(x0+u(b/d))(D/d)
故而其最小解爲 k=(x0D/d)mod(b/d)

3 C++實現

//
//  main.cpp
//  POJ_1061_青蛙的約會
//
//  Created by 黃成林 on 16/3/24.
//  Copyright © 2016年 黃成林. All rights reserved.
//

/**
 * url: http://poj.org/problem?id=1061
 */

#include <cstdio>

long long x0, y0, d;

void gcd(long long a, long long b) {
    if (b == 0) {
        x0 = 1; y0 = 0; d = a;
        return;
    }

    gcd(b, a%b);

    long long temp = x0;
    x0 = y0;
    y0 = temp - a/b*y0;
    return;
}

int main(int argc, const char * argv[]) {
    long long x, y, m, n, L;
    while (scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &L) != EOF) {     
        long long a = m - n;
        long long b = L;
        long long D = y - x;
        if (a < 0) { a = -a; D = -D; }

        gcd(a, b);

        if (0 != D%d) {
            printf("Impossible");
        } else {
            long long k = x0 * D / d;
            long long mod = L / d;
            if (k >= 0) {
                k %= mod;
            } else {
                k = k%mod + mod;
            }
            printf("%lld", k);
        }
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章