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

49 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值