One Person Game(zoj3593+扩展欧几里德)

 One Person Game
Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu
Appoint description:  

Description

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 toa+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

 

 

 

 

 

题意:一维坐标轴,有A和B两个地方,现在从A到B,每次可以向任意方向走a、b或者c的距离,其中c=a+b,问能不能走到B,能的话最少走几次。

 

思路:c = a+b, C = A-B,则等价于求{|x|+|y| | ax+by=C || ax+cy=C || bx+cy=C}。对于ax+by=C,

 

用扩展欧几里得算法求得ax+by=gcd(a,b),是否有解可由C是否为gcd的倍数判断。
 
若有解,原方程的一组解为(x0, y0)  = (xx*C/gcd, yy*C/gcd)。

 

令am=a/gcd,bm=b/gcd,则通解可表示为(x0+k*bm, y0-k*am)。

 

能使|x|+|y|最小的整点一定是最靠近直线与坐标轴交点的地方,

 

可以由|x|+|y|=C的图像不断平移看出。

 

由于负数取整时是先对它的绝对值取整,再添上负号,

 

所以考虑的范围要是[-x0/bm-1, -x0/bm+1] 、[y0/am-1, y0/am+1]。

 

转载请注明出处:http://blog.csdn.net/u010579068/article/details/45487153

 

 

 

#include<stdio.h>
#include<math.h>
#include<algorithm>
#define LL long long
using namespace std;

LL ans;
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 China(LL a,LL b,LL c)
{
    LL x,y,d,bm,am;

    exgcd(a,b,d,x,y);
    if(c%d) return -1;
    bm=b/d;
    am=a/d;
    x=x*c/d;
    y=y*c/d;

 //   printf("x=%lld,y=%lld\n",x,y);
    LL sum=fabs(x)+fabs(y);

    for(int i=-x/bm-1;i<=-x/bm+1;i++)
    {
        LL X=x+bm*i;
        LL Y=y-am*i;
        if(i)
        {
            LL tmp=fabs(X)+fabs(Y);
            if(tmp<sum) sum=tmp;
        }
    }
    for(int i=y/am-1;i<=y/am+1;i++)
    {
        LL X=x+bm*i;
        LL Y=y-am*i;
        if(i)
        {
            LL tmp=fabs(X)+fabs(Y);
            if(tmp<sum) sum=tmp;
        }
    }
    return sum;
}


int main()
{
    int T;
    LL A,B,C,a,b,c,d,x,y;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld%lld",&A,&B,&a,&b);
        c=a+b;
        C=fabs(A-B);
 //       ans==1<<30;
        LL t1=China(a,b,C);
        LL t2=China(a,c,C);
        LL t3=China(b,c,C);

        if(t1==-1&&t2==-1&&t3==-1)
        {
            printf("-1\n");
            continue;
        }
        if(t1>t2) ans=t2;
        else ans=t1;

        if(ans>t3) ans=t3;

        printf("%lld\n",ans);
    }
    return 0;
}


 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值