ZOJ Problem Set - 3593 拓展欧几里得 数学

ZOJ Problem Set - 3593

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3593

One Person Game


Time Limit: 2 Seconds      Memory Limit: 65536 KB


There is an interesting and simple one person game. Suppose there is a number axis under your feet. You are at point A at first and your aim is point B. There are 6 kinds of operations you can perform in one step. That is to go left or right by a,b and c, here c always equals to a+b.

You must arrive B as soon as possible. Please calculate the minimum number of steps.

 

Input

There are multiple test cases. The first line of input is an integer T(0 < T ≤ 1000) indicates the number of test cases. Then T test cases follow. Each test case is represented by a line containing four integers 4 integers ABa and b, separated by spaces. (-231 ≤ AB < 231, 0 < ab < 231)

Output

For each test case, output the minimum number of steps. If it's impossible to reach point B, output "-1" instead.

Sample Input

 

2
0 1 1 2
0 1 2 4

 

Sample Output

1
-1

题解:先求ax+by=A-B的解中|x|+|y|最小的一组x,y值,利用拓展欧几里得可求出ax'+by'=gcd(a,b)的x',y'值,仅在A-B是gcd(a,b)的整数倍时,方程有解,令k=(A-B)/gcd(a,b),则可以求出{\color{Red} x=x'*k+b/gcd(a,b)*t,y=y'*k-a/gcd(a,b)*t},当两条直线相交时|x|+|y|最小,求出交点对应的t,由于交点可能不是整数,所以将t-1,t,t+1各判断一次,若x,y同号,结果取绝对值大的(因为同向时x+y可以合并为1步),若x,y异号,结果取绝对值之和,通过ans记录所有结果中的最小值。

#include<iostream>
#include<cmath>
#include<cstdio>
#define ll long long
using namespace std;
void exgcd(ll a,ll b,ll &gcd,ll &x,ll &y){
        if(!b){
                gcd=a;x=1;y=0;
        }else{
                exgcd(b,a%b,gcd,x,y);
                ll tmp=x;
                x=y;
                y=tmp-a/b*y;
        }
}
int main(){
        int T;
        ll a,b,A,B,x,y,gcd,ans,t;
        scanf("%d",&T);
        while(T--){
                scanf("%lld%lld%lld%lld",&A,&B,&a,&b);
                exgcd(a,b,gcd,x,y);
                //cout<<x<<"  "<<y<<endl;
                if((A-B)%gcd)
                        {printf("-1\n");continue;}
                x=(A-B)/gcd*x;
                y=(A-B)/gcd*y;
                ans=9999999999;
                a/=gcd,b/=gcd;
                t=(y-x)/(a+b);
                //直线x=x+bt与直线y=y-at的距离最近时对应的整数t
                for(int i=t-1;i<=t+1;i++)//t不一定为整数,左右各取一次
                        if(abs(x+i*b)+abs(y-i*a)==abs(x+i*b+y-i*a))//同号
                                ans=min(ans,max(abs(x+i*b),abs(y-i*a)));
                        else//异号
                                ans=min(ans,abs(x+i*b)+abs(y-i*a));
                printf("%lld\n",ans);
        }
        return 0;
}

 

posted @ 2019-04-12 16:09  aeipyuan  阅读(131)  评论(0编辑  收藏  举报