1 擴展歐幾里德算法
擴展歐幾里德算法是用來在已知a, b求解一組x,y,使它們滿足貝祖等式:
ax+by=gcd(a,b)=d (解一定存在,根據數論中的相關定理)。擴展歐幾里德常用在求解模線性方程及方程組中。
gcd: 最大公約數 greatest common divisor
關鍵點
假設
那麼 因爲
反向證明同上。
// 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變換
一一對應,
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”
思路
在兩隻青蛙碰見時會有
其中
令
注意,此處D不一定是a,b的公約數
下面這段推導參考了聖劍的博客
不過沒關係,先用擴展歐幾里得得出
將上面的式子兩邊都除以
因爲d爲a,b的最大公約數,所以a/d,b/d爲整數,所以D/d必定爲整數,否則方程無解;
將下面式兩邊都乘以d/D與上式比較:
得
擴展解
對於方程
因爲d是a,b的gcd, 所以a/d 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;
}