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

题意:一只青蛙在坐标轴的原点,它每次可以向左或向右跳x1或x2的距离,现在,它想跳K次恰好跳到P点,求一个跳的方案,P1为向右跳x1的次数,N1为向左跳x1的次数,P2为向右条x2的次数,P3为向左跳x2的次数。

思路:唉,对数论什么的一直没感觉啊,做了半天好不容易做出来了……首先是求一个可行解,这个简单,相当于解ax+by=c的方程,直接用扩展欧几里得就可以搞定,这里求出的x和y分别相当于P1-N1,P2-N2,剩下是怎么安排次数的问题……首先要把abs(x)+abs(y)的值变到最小,我们知道对于扩展欧几里得求出的解x0,y0来说,x=x0+k*(y0/d)  y=y0-k*(x0/d)都是方程的解那么我们就可以通过这个来调整,令abs(x)+abs(y)的值最小,如果此时abs(x)+abs(y)还是大于k,那么说明无解。否则,看此时还需要走多少歩(也就是k-abs(x)+abs(y)),如果是偶数歩,那么简单,剩下的步数只要不断重复走P1,N1就行了。如果不行的话……我们知道通过上面寻找最小值的方法可以调整x和y的值,此时,如果(y0/d)和(x0/d)的奇偶性不同,那么分别对x和y增加(减少)这两个数就可以改变奇偶性了,否则就无解……


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
void ext_gcd(ll a,ll b,ll & x,ll & y,ll & d)
{
    if(b==0)
    {
        d=a;x=1;y=0;
        return ;
    }
    ext_gcd(b,a%b,y,x,d);
    y-=(a/b)*x;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ll x,y,p,k,d,tmp;
    ll N1,N2,P1,P2,ta,tb;
    cin>>x>>y>>p>>k;
    if(p==0&&k==0)
    {
        cout<<"YES"<<endl;
        cout<<"0 0 0 0"<<endl;
        return 0;
    }
    ext_gcd(x,y,P1,P2,d);
    if(p%d) {cout<<"NO"<<endl;return 0;}
    ta=x/d;tb=y/d;
    P1=P1*(p/d);
    P2=P2*(p/d);
    while(abs(P1)+abs(P2)>abs(P1-tb)+abs(P2+ta)) {P1-=tb;P2+=ta;}
    while(abs(P1)+abs(P2)>abs(P1+tb)+abs(P2-ta)) {P1+=tb;P2-=ta;}
    if(abs(P1)+abs(P2)>k)
    {
        cout<<"NO"<<endl;
        return 0;
    }
    N1=N2=0;
    tmp=k-abs(P1)-abs(P2);
    if(tmp%2==0)
    {
        if(P1<0) swap(P1,N1),N1=-N1;
        if(P2<0) swap(P2,N2),N2=-N2;
        P1+=(tmp/2);
        N1+=(tmp/2);
    }
    else
    {
        if((ta+tb)%2==0)
        {
            printf("NO\n");
            return 0;
        }
        else
        {
            if(abs(P1+tb)+abs(P2-ta)<abs(P1-tb)+abs(P2+ta))
            {
                P1+=tb;
                P2-=ta;
            }
            else
            {
                P1-=tb;
                P2+=ta;
            }
            if(abs(P1)+abs(P2)>k)
            {
                printf("NO\n");
                return 0;
            }
            k-=abs(P1)+abs(P2);
            if(P1<0) swap(P1,N1),N1=-N1;
            if(P2<0) swap(P2,N2),N2=-N2;
            P1+=(k/2);
            N1+=(k/2);
        }
    }
    cout<<"YES"<<endl;
    cout<<P1<<" "<<N1<<" "<<P2<<" "<<N2<<endl;
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值