1. 程式人生 > >ZOJ 3593 One Person Game(ExGcd + 最優解)題解

ZOJ 3593 One Person Game(ExGcd + 最優解)題解

i++ 題解 mes tdi spa code game max include

思路:題意轉化為求 (ax+by=dis) || (ax+cy=dis) || (bx+cy=dis) 三個式子有解時的最小|x| + |y|。顯然求解特解x,y直接用擴展歐幾裏得,那麽怎麽求|x| + |y|?xy關系為一條直線,那麽|x| + |y|應該是在x取0或者y取0的時候,但是要整數,所以只能在周圍取幾個點。我們知道x=x1+b/gcd*t,那麽x1+b/gcd*t = 0可以解得 t = -x1 * gcd / b。然後在附近取幾個點。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include
<cmath> #include<algorithm> #define LS(n) node[(n)].ch[0] #define RS(n) node[(n)].ch[1] using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int maxn = 32767 + 10; ll ex_gcd(ll a, ll b, ll &x, ll &y){ ll d, t; if(b == 0){ x = 1; y = 0;
return a; } d = ex_gcd(b, a%b, x, y); t = x-a/b*y; x = y; y = t; return d; } ll dis; //求|x|+|y|最小 ll solve(ll a, ll b){ ll x, y, d = ex_gcd(a, b, x, y); if(dis % d != 0) return INF; x = x * dis / d; y = y * dis / d; a /= d, b /= d; ll ans = abs(x) + abs(y); ll k; k
= -x / b - 5; for(int i = 0; i <= 10; i++){ ans = min(ans, abs(x + b * (k + i)) + abs(y - a * (k + i))); } k = y / a - 5; for(int i = 0; i <= 10; i++){ ans = min(ans, abs(x + b * (k + i)) + abs(y - a * (k + i))); } return ans; } int main(){ int T; scanf("%d", &T); while(T--){ ll a, b, A, B; scanf("%lld%lld%lld%lld", &A, &B, &a, &b); dis = abs(A - B); ll ans; ans = min(solve(a, a + b), min(solve(a, b), solve(b, a + b))); printf("%lld\n", ans == INF? -1 : ans); } return 0; }

ZOJ 3593 One Person Game(ExGcd + 最優解)題解