ZOJ-3593 One Person Game(數論-擴充套件歐幾里得)
阿新 • • 發佈:2019-01-12
題意
給一個起點A,一個終點B,
從起點出發,
每次可以選擇向左或向右走a格,b格,或c格(c=a+b),
走一次記為一步,求A到B的最小步數,無法走到輸入-1
思路來源
https://blog.csdn.net/yjf3151731373/article/details/70071941
題解
最後肯定是求ax+by=d,d=abs(B-A)
當d不能整除c=gcd(a,b)時,顯然為-1,
令a/=c,b/=c,d/=c,等價於求a'x+b'y=d',gcd(a',b')=1
此時可用a'x+b'y=1求一組解x0,y0,
x0*=d,y0*=d即為一組原方程解x0',y0'
而原方程通解x=x0'+k*b',y=y0'-k*a',
當x與y同號時,答案為max(|x|,|y|);
當x與y異號時,答案為|x|+|y|;
顯然,若x==y能成立,則任何其它解x1,y1都會增大其中一個的絕對值從而使答案更大,
所以如果x==y能成立,答案就是x==y,
此時有x0'+k*b'=y0'-k*a',k=(x0'+y0')/(a'+b')。
這就是一個凹函式,x==y的時候是拐點,
如果k是double的話,一定是取拐點,此時有極小值|x|步
其它情況下,不妨x<y,
若x與y距離不是最小(即存在x1,y1,使得x<x1<y1<y成立時),
x1和y1這組解,無論是同號還是異號,都比x、y這組解更優。
但同樣也有可能,x,y距離是當前最小了(但不是0)(卻存在x1,y1,使得x<y1<y<x1)
而x1,y1這組解更優,這其實是k無法取整從而使距離不能取到理論最小導致的。
若不能取拐點的話,離拐點最近的兩個整點,都判一下就好了
k無法取整值,所以就要取距k最近的那兩個整值判斷一下哪個更小,
那就不妨直接列舉k-1,k,k+1,取最小
程式碼
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> const int maxn=3e3+10; using namespace std; typedef long long ll; int t; ll A,B,a,b; ll extgcd(ll a,ll b,ll &x,ll &y) { ll d=a; if(b)d=extgcd(b,a%b,y,x),y-=(a/b)*x; else x=1,y=0; return d; } int main() { scanf("%d",&t); while(t--) { ll c,d,x,y,ans=1e18; scanf("%lld%lld%lld%lld",&A,&B,&a,&b); d=extgcd(a,b,x,y); if(A<B)swap(A,B);c=A-B; if(c%d) { puts("-1"); continue; } else { x*=(c/d);y*=(c/d); a/=d;b/=d; //a'x+b'y=c' (x,y)一組特解 ll k=(y-x)/(a+b); for(ll i=k-1;i<=k+1;++i) { ll xx=x+b*i,yy=y-a*i,xxx=abs(xx),yyy=abs(yy); if(xx*yy>0)ans=min(ans,max(xxx,yyy)); else ans=min(ans,xxx+yyy); } printf("%lld\n",ans); } } return 0; }