1. 程式人生 > >ZOJ-3593 One Person Game(數論-擴充套件歐幾里得)

ZOJ-3593 One Person Game(數論-擴充套件歐幾里得)

題意

給一個起點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;
}