POJ 1061 青蛙的约会 (扩展欧几里德解不定方程)

题目链接http://poj.org/problem?id=1061 题目大意:两只青蛙在一个首尾相接的轴上(1 - L)跳,并且其中一个起点在x,每步跳m,另一个起点在y,每步跳n,问他们经过某步后可不可以相遇,如果可以,找出步数. 思路: 经典的扩展欧几里德解线性不定方程的题: 显而易见的我们有方程 x + m*p = y + n*p (mod L),但是这个形式不太好解,未知数在两边,那么我们换个形式: (x + m*p) - (y + n*p) = t*L       ->      (n - m) * p + t * L = (x - y) 于是回到了我们熟悉的形式:解不定方程  ax + by = c. 我们用扩展欧几里德来解决这个问题: 1.扩展欧几里德算法解整数不定方程 ax + by = gcd(a, b)  (由定理可知其一定有解,证明略了.) (注:ext_gcd求出方程一个通解x0, y0, 其所有的整数解形式为:(x0 + kb/g, y0 - ka/g)   (g = gcd(a,b) ) ) 首先:根据定理 gcd(a, b) = gcd(b,  a mod b)我们可以得到等式ax1 + by1 = bx2 + (a mod b)y2 整理一下:ax1 + by1 = bx2 + (a - a/b*b)y2    ->     ax1 + by1 = ay2 + b(x2 - a/b * y2) 由恒等定理得: x1 = y2 y1 = x2 - a/b*y2 然后我们在欧几里德辗转相除法的基础上递归地利用gcd(b, a mod b)的解来得到gcd(a, b)的解:  
void ext_gcd(long long a, long long b, long long &x, long long &y){
    if (b == 0){
        x = 1;
        y = 0 ;
        return ;
    }
    ext_gcd(b, a%b, x, y);
    long long tmp = x;
    x = y;
    y = tmp - a/b * y;
    return ;
}
  2.解整数不定方程ax + by = c ★ ①首先由定理"所有的ax + by都一定是gcd(a,b)的倍数"可知方程有解的充要条件是gcd(a,b)必须能整除c. ②然后我们令等式两边同时除以gcd(a,b)得到等价方程   ->   a'x + b'y = c'        易知gcd(a', b') = 1.利用扩展欧几里德求出方程解(x0, y0),则方程a'x + b'y = 1的通解为(x0 + kb, y0 - ka),且(c' * x0, c' * y0)即为方程a'x + b'y = c'的解. ③注:题目要求满足方程的x为解中最小的整数,求最小整数过程中便很容易出现一个问题:如果x#是方程a'x + b'y = 1的最小整数x解,我们不能说c' * x#是方程a'x + b'y = c'的最小整数x解!比如方程413x + 1908y = 365的最小整数x解为(121, -26),看出问题了吧!方程a'x + b'y = c'的所有整数解并不是(  c' * (x0 + kb'),   c' * (y0 - ka')   )!事实上我们应该这么做(虽然我也不会证为什么……):我们求出了a'x + b'y = 1的解(x0, y0)后,先求出a'x + b'y = c'的解(c' * x0, c' * y0),则方程a'x + b'y = c'的所有整数解为(c' * x0 + kb', c' * y0 - ka'). 代码:  
#include 
#include 
using namespace std;
long long gcd(long long a, long long b){
    return b ? gcd(b, a%b) : a;
}
void ext_gcd(long long a, long long b, long long &x, long long &y){
    if (b == 0){
        x = 1;
        y = 0 ;
        return ;
    }
    ext_gcd(b, a%b, x, y);
    long long tmp = x;
    x = y;
    y = tmp - a/b * y;
    return ;
}

int main(){
    long long x, y, m, n, L;
    scanf("%I64d%I64d%I64d%I64d%I64d", &x, &y, &m, &n, &L);
    long long a = n - m;
    long long b = L;
    long long g = x - y;
    if (g % gcd(a,b)){
        printf("Impossible\n");
        return 0;
    }
    long long p = gcd(a, b);
    a /= p;
    b /= p;
    g /= p;
    long long ansx, ansy;
    ext_gcd(a, b, ansx, ansy);
    ansx *= g;
    long long tmp = (long long)abs(double(b));
    ansx = (ansx % tmp + tmp) % tmp;
    printf("%I64d\n", ansx);
    return 0;
}
 
posted @ 2013-01-05 00:41  AbandonZHANG  阅读(161)  评论(0编辑  收藏  举报