HIT2815Min Chain扩展欧几里德

题目:http://acm.hit.edu.cn/hoj/problem/view?id=2815

 

题意:给定两个数,其中,只对进行加减操作,求最少需要多少步能得到1

 

分析:典型的扩展欧几里得算法,注意有些需要特判。


小结:

看到一篇博客说 : a*x+b*y==gcd(a,b)成立的式子的特解:x1,y1,满足:x=x1+t*b,y=y1+t*b,且|x1|<b,|y1|<a

因为题目要求的是|x1|+|y1|最小,故此:当x1<0时比较|x1+b|+|y1-a|和x1,y1 的大小取小的,当x1>0时,比较|x1-b|+|y1+a|和x1,y1 的大小取小的。

对于上面的|x1|<b,|y1|<a这个结论,我也不知道到底是对还是错,网上的题解几乎都采用了这种意思的做法。感觉上貌似是对的,但是也不知道怎么证明。


如果希望更保险的做法的话,还是用手动将x转化为正的最小,转化y时也一样:

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>

using namespace std;
typedef long long LL;

LL gcd(LL a,LL b)
{
    return b ? gcd(b,a%b):a;
}

void extend_Euclid(LL a,LL b,LL &x,LL &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return;
    }
    extend_Euclid(b,a%b,x,y);
    LL tmp = x;
    x = y;
    y = tmp - (a / b) * y;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        LL a,b;
        cin>>a>>b;
        if(a < b) swap(a,b);
        LL g = gcd(a,b);
        if(g != 1)
        {
            puts("-1");
            continue;
        }
        if(b == 1 && a == 2)
        {
            puts("1");
            continue;
        }
        if(b == 1)
        {
            puts("2");
            continue;
        }
        if(b == 0 && a == 1)
        {
            puts("1");
            continue;
        }
        LL x,y;
        extend_Euclid(a,b,x,y);
        LL ans = abs(x) + abs(y);
        if(x < 0)
        {
            x = (x%b + b) % b;
            LL ty = (1-a*x) / b;
            if(ty < 0)
                ty = -ty;
            LL tmp = x + ty;
            if(tmp < ans) ans = tmp;
        }
        else
        {
            y = (y%a + a) % a;
            LL tx = (1-b*y) / a;
            if(tx < 0)
                tx = -tx;
            LL tmp = y + tx;
            if(tmp < ans) ans = tmp;
        }
        cout<<ans - 1<<endl;
    }
    return 0;
}



网上类似于结论性的写法:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b)
{
    return b ? gcd(b, a%b) : a;
}
void extend_Euclid(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return ;
    }
    extend_Euclid(b, a%b, x, y);
    ll tmp = x;
    x = y;
    y = tmp - (a/b)*y;
}
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        ll a, b;
        cin >> a >> b;
        if(a < b) swap(a, b);
        ll g = gcd(a, b);
        if(g != 1)
        {
            puts("-1");
            continue;
        }
        if(a == 2 && b == 1)
        {
            puts("1");
            continue;
        }
        if(b == 1)
        {
            puts("2");
            continue;
        }
        if(b == 0 && a == 1)
        {
            puts("1");
            continue;
        }
        ll x, y;
        extend_Euclid(a, b, x, y);
        ll ans = abs(x) + abs(y);
        if(x < 0)
        {
            ll tmp = abs(x+b) + abs(y-a);
            if(tmp < ans) ans = tmp;
        }
        else
        {
            ll tmp = abs(x-b) + abs(y+a);
            if(tmp < ans) ans = tmp;
        }
        cout << ans - 1 << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值