POJ - 2142 The Balance——扩展欧几里得

题意:

给定a,b,c,求x,y,使得ax + by = c,题目保证有解,多解时另|x|+|y|最小,若还是多解则让|x|*a+|y|*b最小

思路:

首先交换一下a和b保证a>b(注意输出顺序也会受到影响),然后观察式子:

|x|+|y| = |x0+b/gcd(a,b)*t| + |y0 - a/gcd(a, b)*t|

因为我们规定a > b,所以式子的变化率主要由y决定,因此最小值应该在y=0左右,设t=y0*gcd(a,b)/a,那么枚举t-5,t+5之间的t值就能求出结果

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const long long INF = 1e18;
ll iabs(ll x) {
    return (x > 0 ? x : -x);
}
ll exgcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    ll r = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return r;
}
int main() {
    ll a, b, c;
    while (~scanf("%lld%lld%lld", &a, &b, &c) && (a || b || c)) {
        bool flag = false;
        if (a < b) {
            swap(a, b);
            flag = true;
        }
        ll x, y;
        ll d = exgcd(a, b, x, y);
        x *= c / d, y *= c / d;
        ll t = y * d / a;
        ll minv = INF, minsum = INF, ansx = 0, ansy = 0;
        for (ll i = t - 5; i <= t + 5; i++) {
            ll xx = iabs(x + b / d * i), yy = iabs(y - a / d * i);
            if (xx + yy < minv) {
                minv = xx + yy;
                minsum = xx * a + yy * b;
                ansx = xx;
                ansy = yy;
            }
            else if (xx + yy == minv) {
                if (xx * a + yy * b < minsum) {
                    minsum = xx * a + yy * b;
                    ansx = xx;
                    ansy = yy;
                }
            }
        }
        if (flag) printf("%lld %lld\n", ansy, ansx);
        else printf("%lld %lld\n", ansx, ansy);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hao_zong_yin/article/details/80273248