扩展欧几里得算法练习题

  • ZOJ3609
  • ZOJ3593
  • POJ1061
  • HDU1576
  • HDU2669
  • UVA12169
ZOJ3609 Modular Inverse
题解

求最小的正逆元,直接用扩展欧几里得就行,注意特殊点,当 ax≡ 1 (mod 1)时。逆元为0,但是要求正逆元,所以要在判断时是 <=0

代码
#include
#include
using namespace std;

int exgcd( int a,int b, int &x, int &y )
{
    int r = a % b;
    int x0, y0, x1, y1;
    x0 = 1; y0 = 0;
    x1 = 0; y1 = 1;
    x = x1, y = y1;
    while( r )
    {
        x = x0 - a / b * x1;
        y = y0 - a / b * y1;
        x0 = x1;
        y0 = y1;
        x1 = x;
        y1 = y;
        a = b;
        b = r;
        r = a % b;
    }
    return b;
}

int main()
{
    int T, a, b, x, y, d;
    scanf( "%d", &T );
    while( T -- )
    {
        scanf( "%d%d", &a, &b );
        int d = exgcd( a, b, x, y );
        if( d > 1 ) printf( "Not Exist\n" );
        else printf( "%d\n", x > 0 ? x : x + b );
    }
    return 0;
}
ZOJ3593 One Person Game
知识

不定方程ax + by = c的通解
先求出特解x0,y0
通解
x = x0 - ( b / gcd( a, b ) ) * t
y = y0 + ( a / gcd( a, b ) ) * t;

题解

转换为方程式 ax + by + cz = d 求min( |x|+|y|+|z| );

因为 c = a + b;所以可以转化为3个二元不定方程
ax + by = d , by + cz = d, ax + cz = d,所以可以使用扩展欧几里得求ax + by = d
求min(|x|+|y|)
因为 x = x0 - ( b / gcd(a, b) ) * t, y = y0 + ( a / gcd( a, b ) ) * t;
最小值在3个位置 x = 0, y = 0 x = y处
因为是整数,所以 求出每个位置的值tmp后,还要求tmp - 1, tmp + 1,然后再计算最小值,
可能这题的测试数据太弱了。感觉网上很多程序都不对。。

代码
#include
#include
#include
using namespace std;

long long C;

long long exgcd( long long a,long long b, long long &x, long long &y )
{
    long long r = a % b;
    long long x0, y0, x1, y1;
    x0 = 1; y0 = 0;
    x1 = 0; y1 = 1;
    x = x1, y = y1;
    while( r )
    {
        x = x0 - a / b * x1;
        y = y0 - a / b * y1;
        x0 = x1;
        y0 = y1;
        x1 = x;
        y1 = y;
        a = b;
        b = r;
        r = a % b;
    }
    return b;
}

long long solve( long long m, long long n )
{
    long long x0, y0, goal, tmp, xx, yy;
    long long d = exgcd( m, n, x0, y0 );
    if( C % d ) return -1;
    x0 *= ( C / d );
    y0 *= ( C / d );
    long long kx = x0 / n * ( -1 );
    long long mm = m / d;
    long long nn = n / d;
    //case 1
    tmp = x0 / nn ;
    xx = x0 - tmp * nn;
    yy = y0 + tmp * mm;
    goal = abs( xx ) + abs ( yy );
    tmp ++;
    xx = x0 - tmp * nn;
    yy = y0 + tmp * mm;
    goal = min( abs( xx ) + abs ( yy ), goal );
    tmp -= 2;
    xx = x0 - tmp * nn;
    yy = y0 + tmp * mm;
    goal = min( abs( xx ) + abs ( yy ), goal );
    //case 2
    tmp = y0 / mm;
    xx = x0 + tmp * nn;
    yy = y0 - tmp * mm;
    goal = min( abs( xx ) + abs ( yy ), goal );
    tmp ++;
    xx = x0 + tmp * nn;
    yy = y0 - tmp * mm;
    goal = min( abs( xx ) + abs ( yy ), goal );
    tmp -= 2;
    xx = x0 + tmp * nn;
    yy = y0 - tmp * mm;
    goal = min( abs( xx ) + abs ( yy ), goal );
    //case 3
    tmp = abs( x0 - y0 ) / abs( nn + mm );
    xx = x0 - tmp * nn;
    yy = y0 + tmp * mm;
    goal = min( abs( xx ) + abs ( yy ), goal );
    tmp ++;
    xx = x0 - tmp * nn;
    yy = y0 + tmp * mm;
    goal = min( abs( xx ) + abs ( yy ), goal );
    tmp -= 2;
    xx = x0 - tmp * nn;
    yy = y0 + tmp * mm;
    goal = min( abs( xx ) + abs ( yy ), goal );
    return goal;
}

int main()
{
    int T;
    long long a, b, c, A, B;
    scanf( "%d", &T );
    while( T -- )
    {
        scanf( "%lld%lld%lld%lld", &A, &B, &a, &b );
        c = a + b;
        C = A - B;
        long long Min = solve( a, b );
        Min = min( Min, solve( a, c ) );
        Min = min( Min, solve( b, c ) );
        printf( "%lld\n", Min );
    }
    return 0;
}

你可能感兴趣的:(扩展欧几里得算法练习题)