题目: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;
}