ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)

ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)

题意

你要从A走到B,你每次可以走a步,b步,a+b步问最小需要走多少步?无法到达输出 -1。

题解

先不考虑a+b步的情况,那么我们要求解的就是:ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程),如果ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程),证明无解。

假设原方程一组解为x0,y0,那么通解(x,y)为:ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)
其实也就是两条直线:ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)

取一条平行于y轴的直线 x = t :

如果 x 和 y 异号,假设x > 0,y < 0也就是往前走x次a步,往后走y次b步。x < 0, y > 0同理,这种情况答案为ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)

如果 x 和 y 同号,其实也就是都往前(后)走x次a步,y次b步,考虑上可以走a+b的情况,答案也就是ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)

结合图可知,越接近交点值越小,当 t 取 x 和 y 交点时,答案最小。

ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程) =》ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程) ,但是 t 可能不是整数,所以我们要尝试 t-1,t,t+1 三个值。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
void exgcd(LL a, LL b, LL& d, LL& x, LL& y)
{
    if (!b) { d=a; x=1; y=0; }
    else { exgcd(b, a%b, d, y, x); y -= x*(a/b); }
}
LL cal(LL x, LL y)
{
    if (x*y >= 0) return max(abs(x), abs(y));
    else return abs(x)+abs(y);
}
LL solve(LL a, LL b, LL c)
{
    LL GCD, x0, y0;
    exgcd(a, b, GCD, x0, y0);
    if (c%GCD) return -1;
    x0 *= c/GCD; y0 *= c/GCD;

    LL aa = a/GCD, bb = b/GCD;
    LL t = (y0-x0)/(aa+bb);//x0+a't=y0-b't,t = (y0-x0)/(a'+b')
    LL ans1 = cal(x0+bb*t, y0-aa*t);
    LL ans2 = cal(x0+bb*(t-1), y0-aa*(t-1));
    LL ans3 = cal(x0+bb*(t+1), y0-aa*(t+1));
    return min(min(ans1, ans2), ans3);
}
int main()
{
    int T; scanf("%d", &T);
    while (T--)
    {
        LL A, B, a, b; scanf("%lld%lld%lld%lld", &A, &B, &a, &b);
        LL ans = solve(a, b, abs(A-B));
        printf("%lld\n", ans);
    }
    return 0;
}
/*
2
0 1 1 2
0 1 2 4
*/