扩展欧几里求最优解

https://zoj.pintia.cn/problem-sets/91827364500/problems/91827369176

题意:有一数轴 , 问从A点到B点最少需要几步。可以有6中方式行走,左右a , b a+b.

解法:设走了x步a , y步b。有方程 ax + by = B-A ,转化为求|x| + |y|的最小值,x , y 分别是两条直线。

|x|+|y|最小值在x=x+bt与y=y-at两条直线交点附近。

#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define cin(a) scanf("%d",&a)
#define pii pair<int,int>
#define ll long long
#define gcd __gcd
#define mod 1000000007
#define INF  0x3f3f3f3f
const ll inf = 0x3f3f3f3f;
const int M = 1e9+7;
const int N = 100009 ;
ll x , y , d ;

void gcd(ll a , ll b , ll &d , ll &x , ll &y)
{
    if(b == 0)
    {
        x = 1 ;
        y = 0 ;
        d = a ;
    }
    else{
        gcd(b , a%b , d , x , y);
        ll t = x ;
        x = y ;
        y = t - (a/b) * y ;
    }
}

ll cal(ll a , ll b , ll c)
{
    gcd(a , b , d , x , y);
    if(c % d) return -1 ;
    else{
        return 1;
    }
}

int main()
{
    int t ;
    scanf("%d" , &t);
    while(t--)
    {
        ll A , B , a , b ;
        scanf("%lld%lld%lld%lld" , &A,&B , &a, &b);
        ll c = B - A ;
        ll t = cal(a , b , c);
        if(t == -1){
            cout << -1 << endl ;
            continue ;
        }
        else
        {
            x *= c / d ;
            y *= c / d ;
            a /= d ;
            b /= d ;
            ll ans = inf * inf , tmp ;//inf要用ll,在这wa了几发
            ll mid = (y - x) / (a + b);//mid为两直线交点
            for(ll T = mid - 1 ; T <= mid + 1 ; T++)
            {
                if((x + b*T)*(y - a*T) >= 0)//x ,y 同号
                {
                    tmp = max(abs(x+b*T) , abs(y-a*T));//取最大那个就可以,相同部分取x+y每步
                }
                else{
                    tmp = abs(x - y + (a + b) * T);//都取
                }
                ans = min(tmp , ans);
            }
            cout << ans << endl ;
        }

    }

    return 0;
}
posted @ 2020-01-09 15:11  无名菜鸟1  阅读(163)  评论(0编辑  收藏  举报