您的位置:首页 > 其它

SGU 141 Jumping Joe(扩展欧几里得)

2016-04-16 18:58 375 查看
Description

给出x1,x2,p,k,求一组非负整数解(p1,n1,p2,n2)满足:

p1+n1+p2+n2=k,(p1-n1)*x1+(p2-n2)*x2=p

Input

四个整数x1,x2,p,k(0< x1,x2< 40000,-40000< p< 40000,0<=k< 200000000)

Output

如果存在一组非负整数解则输出YES以及这组解,否则输出NO

Sample Input

2 3 -1 12

Sample Output

YES

1 0 5 6

Solution

令x=p1-n1,y=p2-n2,首先用扩展欧几里得求出x1*x+x2*y=g的一组解(x,y),其中g=gcd(x1,x2)

令x=x*p/g,y=y*p/g,dx=x2/g,dy=x1/g,则通解为X=x+dx*k,Y=y-dy*k

进而可以算出使得abs(X)+abs(Y)最小的X和Y

若abs(X)+abs(Y)>K则无解,因为此时的X和Y是最小需要的步数

若K-abs(X)-abs(Y)为偶数,则将这些剩下的步数分为两半,来回各走一半就可解决

若K-abs(X)-abs(Y)为奇数,那么我们需要通过加减dx和dy使得K-abs(X)-abs(Y)为偶数,此时dx+dy为偶数时无解,否则,X和Y再加减或减加一次dx和dy就可以改变K-abs(X)-abs(Y)的奇偶性

Code

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll extend_gcd(ll a,ll b,ll &x,ll &y)
{
ll d=a;
if(b!=0)
{
d=extend_gcd(b,a%b,y,x);
y-=(a/b)*x;
}
else
{
x=1;
y=0;
}
return d;
}
int main()
{
ll x1,x2,g,p,k,p1,n1,p2,n2;
scanf("%lld%lld%lld%lld",&x1,&x2,&p,&k);
g=extend_gcd(x1,x2,p1,p2);
if(p%g)
{
printf("NO\n");
return 0;
}
ll dx=x2/g,dy=x1/g;
p1*=p/g,p2*=p/g;
while(abs(p1+dx)+abs(p2-dy)<abs(p1)+abs(p2))p1+=dx,p2-=dy;
while(abs(p1-dx)+abs(p2+dy)<abs(p1)+abs(p2))p1-=dx,p2+=dy;
if(abs(p1)+abs(p2)>k)
{
printf("NO\n");
return 0;
}
n1=n2=0;
ll last=k-abs(p1)-abs(p2);
if(last%2==0)
{
if(p1<0)n1=-p1,p1=0;
if(p2<0)n2=-p2,p2=0;
p1+=last/2,n1+=last/2;
}
else
{
if((dx+dy)%2==0)
{
printf("NO\n");
return 0;
}
if(abs(p1+dx)+abs(p2-dy)<abs(p1-dx)+abs(p2+dy))p1+=dx,p2-=dy;
else p1-=dx,p2+=dy;
if(abs(p1)+abs(p2)>k)
{
printf("NO\n");
return 0;
}
last=k-abs(p1)-abs(p2);
if(p1<0)n1=-p1,p1=0;
if(p2<0)n2=-p2,p2=0;
p1+=last/2,n1+=last/2;
}
printf("YES\n");
printf("%lld %lld %lld %lld\n",p1,n1,p2,n2);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: