青蛙的约会【拓展欧几里得算法】【详细思路以及推导过程】【萌新必会系列】

在上这道题之前,先给大家看个精彩的:

青蛙的约会【拓展欧几里得算法】【详细思路以及推导过程】【萌新必会系列】_第1张图片

这道玄学的题目,让我整整花费了八个小时去推导以及去了解拓展欧几里得,从一个萌新到真正可以理清自己的思路了。

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。 
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。 
Input
输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。
Output
输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"
Sample Input
1 2 3 4 5
Sample Output
4

        这是学长挂给我们的一道题,重在让我们理清欧几里得算法的作用以及灵活运用它。

        思路:遇上这道题,我们先列写出方程,我们可以知道:x+m*t-y-n*t=p*L(p为转了几圈)

很显然,这是一道拓展欧几里得算法,然后我们针对这道题开始讲解什么是欧几里得算法:

        假如存在这样的a*x+b*y=d的线性方程,我们想知道它的通解怎样实现,那么我们就举个例子吧:

        a*x+b*y=d,如果d mod gcd(a, b)==0

        则,a*x1+b*y1=d; b*x2+a%b*y2=d; 整理得b*x2+(a-(a/b)*b)*y2=d;

        于是一一对应前式a、b所对应点有,x1=y2; y1=x2-(a/b)*y2。

那么,我们由此可以写出拓展欧几里得公式:

ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll g=exgcd(b, a%b, x, y);
    ll temp=x;
    x=y;
    y=temp-y*(a/b);
    return g;
}

当然,这是一种四个参数的,还有种五个参数的:

void exgcd(ll a, ll b, ll &d, ll &x, ll &y)
{
    if(b==0)
    {
        d=a;
        x=1;
        y=0;
        return;
    }
    exgcd(b, a%b, d, y, x);
    y-=a/b*x;
}

        现在,我们要做一个判断:方程是否有解

    拓展欧几里得公式存在,就是指a*x+b*y=d;而d%gcd(a, b)==0,那么假如d%gcd(a, b)!=0,则等式不成立。

判断条件

if(d%gcd(a, b)!=0) 不存在解

        寻找结果answer:

        直接输出 exgcd(a, b, &x, &y)中的x?当然是不行的,我们求得的x为其中的一个特解,不一定是最佳的答案,而且还有负值的可能,所以我们要做些处理:

对于一个x数取到0~r中的值,我们可以这么操作:x=((x%r)+r)%r

那么容我证明一下这个r的取值:

        a*x0+b*y0=d; 又假设还存在这样的x1、y1:a*x1+b*y1=d;

        则,a*(x1-x0)=b*(y0-y1); a/gcd(a, b)*(x1-x0)=b/gcd(a, b)*(y0-y1);

a/gcd(a, b)与b/gcd(a, b)互质,则(x1-x0)与b/gcd(a, b)成正比,(y0-y1)与a/gcd(a, b)成正比

所以能得到:x=x0+K*b/gcd(a, b)其中K为一切整数,y=y0+K*a/gcd(a, b)其中K为一切整数。

那么,对于x而言,r=b/gcd(a, b)。

ll want(ll x0, ll r0)
{
    return ((x0%r0)+r0)%r0;
}

捋通了思路之后,我们就可以知道了:我们对于公式:(n-m)*t+L*P=x-y;分别将他们看作线性方程的a,b,x,y,即可求得正解。

完整代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b)
{
    if(b==0)return a;
    return gcd(b, a%b);
}
ll x,y,m,n,L;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll g=exgcd(b, a%b, x, y);
    ll temp=x;
    x=y;
    y=temp-y*(a/b);
    return g;
}
ll want(ll x0, ll r0)
{
    return ((x0%r0)+r0)%r0;
}
int main()
{
    while(scanf("%lld %lld %lld %lld %lld",&x,&y,&m,&n,&L)!=EOF)
    {
        ll t,p;
        ll a1,b1,d1;
        d1=x-y;
        b1=L;
        a1=n-m;
        ll k=gcd(a1, b1);
        if(d1%k!=0)
        {
            printf("Impossible\n");
            continue;
        }
        exgcd(a1, b1, t, p);
        ll r=b1/gcd(a1, b1);
        printf("%lld\n",want(t*d1/k, r));
    }
    return 0;
}

你可能感兴趣的:(拓展欧几里得算法,拓展欧几里得算法)