SGU141 Jumping joe

又用到了擴展歐幾里得,這題其實主要是手推公式... 草稿紙被我扔進垃圾桶N次,又被我撿回來N次...T_T

首先發現 (p1-n1)x1+(p2-n2)x2 =p,又是整數解,聯想擴展歐幾里得

p1-n1=s

p2-n2=t

p1+n1+p2+n2=k

p1>=0,p2>=0,n1>=0,n2>=0

進一步推得

2*(n1+n2)=k-s-t

n1>=max(0,-s)

n2>=max(0,-t)

再推

2*(n1+n2)>=2*(max(0,-s)+max(0,-t))

2*(n1+n2)=k-s-t

問題轉換爲判斷是否存在n1,n2滿足上面兩個式子,也就是判斷是否存在s,t 使得 k-s-t>=2*(max(0,-s)+max(0,-t))

看起來好像還是很複雜... 繼續推一下,討論s,t的正負。

討論過後發現問題變成了找s,t 使得  k>=fabs(s)+fabs(t)

我們利用擴展歐幾里得已經求得了兩個特解s0,t0 而s=s0+k'*x2/gcd(x1,x2) ;t=t0-k'*x1/gcd(x1,x2),其實直接讓我求最值我沒想出來怎麼做...但是我們可以枚舉k',從-40000到40000枚舉一遍,求得最值(這...全憑既視感= =|||),找最值的過程注意還要滿足k-s-t是2的倍數這個條件。

找到最值後,判斷一下k>=fabs(s)+fabs(t)即可。

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
void ex_gcd(LL a,LL b,LL &x,LL &y,LL &d)
{
    if(b==0)
    {
        x=1,y=0,d=a;
        return ;
    }
    else
    {
        ex_gcd(b,a%b,x,y,d);
        int t=x;
        x=y;
        y=t-a/b*y;
        return ;
    }
}
int main()
{
    LL x1,x2,p,k,p1,n1,p2,n2,s,t,d,tt1,tt2,m,fs,ft,i;
    bool find;
    scanf("%lld %lld %lld %lld",&x1,&x2,&p,&k);
    ex_gcd(x1,x2,s,t,d);
    if(p%d!=0)
    {
        printf("NO\n");
        return 0;
    }
    s*=p/d,t*=p/d;
    tt1=x2/d,tt2=x1/d;
    find=false;
    s-=40000*tt1;t+=40000*tt2;
    for(i=-40000;i<=40000;++i)
    {
        if((k-s-t)%2==0&&(!find||fabs(s)+fabs(t)<m))
        {
            m=fabs(s)+fabs(t);
            fs=s;
            ft=t;
            find=true;
        }
        s+=tt1,t-=tt2;
    }
    if(find&&k>=m)
    {
        printf("YES\n");
        n1=max(0LL,-fs);n2=(k-fs-ft-2*n1)/2;p1=fs+n1;p2=ft+n2;
        printf("%lld %lld %lld %lld\n",p1,n1,p2,n2);
    }
    else
    {
        printf("NO\n");
    }
    return 0;
}


 

發佈了88 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章