ZOJ 3593 One Person Game

令c = a+b, d = A-B,則等價於求{|x|+|y| | ax+by=d || ax+cy=d || bx+cy=d}。對於ax+by=d,用擴展歐幾里得算法求得axx+byy=gcd(a,b),是否有解可由d是否爲gcd的倍數判斷。若有解,原方程的一組解爲(x0, y0)  = (xx*d/gcd, yy*d/gcd)。令aa=a/gcd,bb=b/gcd,則通解可表示爲(x0+k*bb, y0-k*aa)。能使|x|+|y|最小的整點一定是最靠近直線與座標軸交點的地方,可以由|x|+|y|=C的圖像不斷平移看出。由於負數取整時是先對它的絕對值取整,再添上負號,所以考慮的範圍要是[-x0/bb-1, -x0/bb+1], [y0/aa-1, y0/aa+1]。


#include <cstdio>
#define ll long long

inline void Min(ll& x, ll y)
{
	if(x < 0) x = y;
	if(y < 0) return;
	if(y < x) x = y;
}

inline ll Abs(ll x)
{
	return x<0?-x:x;
}

inline ll Sum(ll x, ll y)
{
	return Abs(x)+Abs(y);
}

ll exgcd(ll a,ll b,ll& x,ll& y)
{
	ll t, ret;
	if (! b)
	{
		x=1, y=0;
		return a;
	}
	ret = exgcd(b, a%b, x, y);
	t = x, x = y, y = t - a/b*y;
	return ret;
}

ll a, b, c, d, A, B, T;
ll x, y, gcd, k, min, sum;

ll solve(ll a, ll b)
{
	gcd = exgcd(a, b, x, y);
	if(d%gcd != 0)
		return -1;
	x = x*(d/gcd);
	y = y*(d/gcd);
	a /= gcd;
	b /= gcd;
	
	min = Sum(x, y);
	k = -x/b-1;
	sum = Abs(x+k*b)+Abs(y-k*a);
	Min(min, sum);
	k += 1;
	sum = Abs(x+k*b)+Abs(y-k*a);
	Min(min, sum);
	k += 1;
	sum = Abs(x+k*b)+Abs(y-k*a);
	Min(min, sum);
	k = y/a-1;
	sum = Abs(x+k*b)+Abs(y-k*a);
	Min(min, sum);
	k += 1;
	sum = Abs(x+k*b)+Abs(y-k*a);
	Min(min, sum);
	k += 1;
	sum = Abs(x+k*b)+Abs(y-k*a);
	Min(min, sum);
	return min;
}

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章