青蛙的约会(扩展欧几里得模板题(模板已升级))

79 篇文章 0 订阅
73 篇文章 0 订阅


Link:http://poj.org/problem?id=1061


青蛙的约会
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 98118 Accepted: 18589

Description

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。 
我们把这两只青蛙分别叫做青蛙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

Source



编程思想:依题意,可推出公式为 (x + m * s) - (y + n * s) = k * L(k = 0, 1, 2,...),公式化简,得 (n-m) * s + k * l = x - y。令a= (n-m),x=s,b= l ,y=k,c=x - y,即转化为模板题,判断a * x + b * y = c,是否存在整数解。可用扩张欧几里得求解该线性方程。

下面代码中的扩张欧几里得模板来自:http://blog.csdn.net/zhengnanlee/article/details/11607755


AC code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#define PI acos(-1.0)
#define LINF 1000000000000000000LL
#define eps 1e-8
#define LL long long
#define MAXN 100010 
using namespace std;
const int INF=0x3f3f3f3f;
LL gcd(LL a, LL b)
{
    return b ? gcd(b, a%b) : a;
}

//find x, y that satisfied the equation ax+by=d, which minimize the {|x|+|y|}. ps:d = gcd(a,b).
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{
    if (!b)
    {
        d = a, x = 1, y = 0;
    }
    else
    {
        exgcd(b, a %b, d, y, x);
        y -= x * (a / b);
    }
}
//1、先计算Gcd(a, b),若n不能被Gcd(a, b)整除,则方程无整数解;否则,在方程两边同时除以Gcd(a, b),得到新的不定方程a' * x + b' * y = n',此时Gcd(a', b')=1;
//2、利用上面所说的欧几里德算法求出方程a' * x + b' * y = 1的一组整数解x0, y0,则n' * x0,n' * y0是方程a' * x + b' * y = n'的一组整数解;
//3、根据数论中的相关定理,可得方程a' * x + b' * y = n'的所有整数解为:
//x = n' * x0 + b' * t
//y = n' * y0 - a' * t
//(t为整数)
bool getans(LL a, LL b, LL c, LL &ans)// ax + by = c 最小整数解
{
    LL r = gcd(a, b), y0;
    if (c%r)//no solutions
    {
        return false;
    }

    a /= r, b /= r, c /= r;

    exgcd(a, b, r, ans, y0);//至此,上面的说明解决了

    LL t = c * ans / b;
    ans = c * ans - t * b;

    /*此时方程的所有解为:x = c*ans - b*t, x的最小的可能值是0
    令x = 0可求出当x最小时的t的取值,但由于x = 0是可能的最小取值,实际上可能x根本取不到0
    那么由计算机的取整除法可知:由 t = c*k1 / b算出的t
    代回x = c*ans - b*t中,求出的x可能会小于0,此时令t = t + 1,求出的x必大于0;
    如果代回后x仍是大于等于0的,那么不需要再做修正。*/

    if (ans < 0)
    {
        ans += b;
    }
    return true;
}
LL x,y,m,n,L,a,b,c,ans;
bool fg;
int main()
{
	while(scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L)!=EOF)
	{
		a=n-m;
		b=L;
		c=x-y;
		fg=getans(a,b,c,ans);
		if(!fg)
		{
			printf("Impossible\n");
		}
		else
		{
			printf("%lld\n",ans);
		}
	}
    return 0;
}


扩张欧几里得模板升级版:

AC code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#define PI acos(-1.0)
#define LINF 1000000000000000000LL
#define eps 1e-8
#define LL long long
#define MAXN 100010 
#define MOD 1000000007
using namespace std;
const int INF=0x3f3f3f3f;
LL exgcd(LL A,LL &x,LL B,LL &y)
{
    LL x1,y1,x0,y0;
    x0=1;y0=0;
    x1=0;y1=1;
    LL r=(A%B+B)%B;
    LL q=(A-r)/B;
    x=0;y=1;
    while(r)
    {
        x=x0-q*x1;
        y=y0-q*y1;
        x0=x1;
        y0=y1;
        x1=x;y1=y;
        A=B;B=r;r=A%B;
        q=(A-r)/B;
    }
    return B;
}
LL x,y,m,n,L,a,b,c,ans;  
int main()
{
    while(scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L)!=EOF)  
    {
    	a=n-m;
    	b=L; 
        c=x-y; 
        LL x0,y0,flag=0,x,y,min_x,min_y;
        if(a<b){flag=a;a=b;b=flag;flag=1;}//默认令a>=b,flag=1说明有交换,则对应的系数x和y最后输出时也要交换后输出 
        int d=exgcd(a,x0,b,y0);//返回a、b的最大公约数 
        //求ax+by=c 
        if(c%d!=0)//无解 
        {
        	printf("Impossible\n");
		}
		else
		{
			x0=x0*(c/d);//x的一个解
	        y0=y0*(c/d);//y的一个解
	        LL t=y0/(a/d),minx=INF,miny=INF;
	        for(int i=t-1;i<=t+1;i++)
	        {
	        	//x的所有解是x=x0+b/d*t,y的所有解是:y=y-a/d*t。
	            x=x0+b/d*i;
	            y=y0-a/d*i;
	            /*if(fabs(x)+fabs(y)<min)
	            {
	                min=fabs(x)+fabs(y);
	                min_x=fabs(x);
	                min_y=fabs(y);
	            }*/
	            //printf("%d %d\n",x,y);  
	            if(x>=0)
	            {
	            	minx=min(minx,x);
				}
	            if(y>=0)
	            {
	            	miny=min(miny,y);
				}
	        }
	        /*if(flag)printf("%lld %lld\n",min_y,min_x);
	        else printf("%lld %lld\n",min_x,min_y);*/
	        if(flag)printf("%lld\n",miny);
	        else printf("%lld\n",minx);
	        //printf("%lld\n",minx);  
		}
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林下的码路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值