ZOJ ~ 3593 ~ One Person Game (扩展欧几里得,不定方程)
题意
你要从A走到B,你每次可以走a步,b步,a+b步问最小需要走多少步?无法到达输出 -1。
题解
先不考虑a+b步的情况,那么我们要求解的就是:,如果
,证明无解。
假设原方程一组解为x0,y0,那么通解(x,y)为:,
。
其实也就是两条直线:,
取一条平行于y轴的直线 x = t :
如果 x 和 y 异号,假设x > 0,y < 0也就是往前走x次a步,往后走y次b步。x < 0, y > 0同理,这种情况答案为
如果 x 和 y 同号,其实也就是都往前(后)走x次a步,y次b步,考虑上可以走a+b的情况,答案也就是。
结合图可知,越接近交点值越小,当 t 取 x 和 y 交点时,答案最小。
=》
,但是 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
*/