前几天参加比赛的一道小编程题
前几天参加了一个编程比赛,有下面这么一小题,当时写了一个算法,能运算出结果,但是运算量相当大,所有数据必须声明为long int类型。觉得还是蛮有意思的一道题,所以放上来让大家一起想想办法。本人的思路是:
(1)首先算出A青蛙和B青蛙各跳多少次以后能分别回到初始位置。
(2)然后求出这两个次数的最小公倍数,那就是他们同时回到初始位置所需要跳跃的最小次数n。
(3)此时可进行位移的等式判定,如果在上面求出的n次之内,他们都没有碰面的话,那就说明他们永远碰不了面,输出impossible。
回来查了下网上有些算法,都是把n定义为10万或是100万甚至更多。个人认为这是不严谨的,因为你无法证明,他们在跳第10万零一次时不会相遇。不知道大家都有些什么好想法呢~~
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" 都一天啦……没人有算法么……:(eew 我做不出
:lol不过网上有的 我做不出
:lol不过网上有的
zapline 发表于 2009-12-3 22:20 http://www.52pojie.cn/images/common/back.gif
网上我查过了,都是给n一个预定义的数,这样肯定是不行的,L的取值范围可以到21亿的单位米,如果L取21亿米,m,n取1米和2米的话,或者0.1米,0.2米的话,n的大小是无法确定的。惆怅啊~~曾经想过采用反证的方法,假设他们能在某点碰头,然后计算他们是否能回到初始位置,可惜算法设计失败…… PKU1061 解题报告 青蛙的约会 __用扩展欧几里得解模同余方程 收藏
题目描述:
两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。
输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。
输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"
Sample Input:
1 2 3 4 5
Sample Output:
4
题目分析:
根据题意可知,题目要求的是求(x+k*m)mod l=(y+k*n)mod l这个模方程的k的最小整数解;跟据模的性质对这个方程变形得:k*(m-n)+t*l=y-x(t为引进的一整数);
令a=(m-n);b=y-x;得:
k*a+t*l=b; 0)
看到这个式子是不是感觉很眼熟,对,它就是扩展欧几里得法能解的标准方程a*x+b*y=d(此a,b非彼a,b)的标准形;唯一的不同是扩展欧几里得d是前面两系数a,b的最大公约数;而0)式中的b就不能保证了。
不过没关系,先用扩展欧几里得得出k*a+t*l=gcd(k,l)的一个解k0,t0;再观察两个式子:
k*a+t*l=b 0)
k0*a+t0*l=d 1)(令d=gcd(k,l)));
将0)式两边都除以d,得:
k*(a/d)+t*(l/d)=b/d;
因为d为a,b的最大公约数,所以a/d,l/d为整数,所以b/d必定为整数,否则方程无解;
将1)式两边都乘以b/d与0)式比较:
k*a+t*l=b0)
(k0*b/d)*a+t0*(b/d)*t=b 1)
得k=k0*b/d;t=t0*b/d;
接下来就是由一个解扩展成一个解系:
1)除以d得: k0*(a/d)+t0*(l/d)=1;
因为(a/d)和(l/d)互质,将方程写成:
(k0+u*(l/d))*(a/d)+(t0+v*(a/d))*(l/d)=1;
只要u*(l/d)*(a/d)+v*(a/d)*(l/d)=0成立就行;
所以k,t的解系可以写成k=(k0+u*(l/d))*(b/d);t=(t0+v*(a/d))*b/d;
接下来就是求k的最小整数解了;
因为k=(k0 mod (l/d)+(u+k0/(l/d))*(l/d))*(b/d);
所以kmin=(k0*(b/d)) mod (l/d);
大功基本高成了,还差最后一步,那就是如何用扩展欧几里得解k*a+t*l=gcd(k,l):
对于a*x+b*y=d;
递归调用时
令a=b;b=a%b;
将其变为形式2) b*x+a%b*y=d;
变形:b*x+a*y-(a/b)*b*y=c;
再变:a*y+b(x-a/b*y)=c; 3)
与2)式比较:b*x+a%b*y=c; 2)
得:
当a=b;b=a%b时:x=y;y=x-a/b*y;
调用过程中的x,y就是对应的a,b的解;
当回到顶层时,a,b就是最初的a,b所以此时的x,y就是所求解;
函数写法如下:(解保存在全局变量k,t中)
__int64 extended_gcd(__int64 a,__int64 b)
{
if (b==0)
{
k=1;
t=0;
d=a;
return a;
}
else
{
__int64 tp_gcd;
tp_gcd=extended_gcd(b,a%b);
__int64 temp;
temp=k;
k=t;
t=temp-(a/b)*t;
return tp_gcd;
}
}
附上AC代码:
#include<stdio.h>
__int64 k,t,d;
__int64 extended_gcd(__int64 a,__int64 b)
{
if (b==0)
{
k=1;
t=0;
d=a;
return a;
}
else
{
__int64 tp_gcd;
tp_gcd=extended_gcd(b,a%b);
__int64 temp;
temp=k;
k=t;
t=temp-(a/b)*t;
return tp_gcd;
}
}
int main()
{
__int64 x,y,m,n,a,b,aa,ll,l;
while(scanf("%I64d %I64d %I64d %I64d %I64d",&x,&y,&m,&n,&l)!=EOF)
{
a=m-n;b=y-x;
if (a<0) {a=-a;b=-b;}
extended_gcd(a,l);
if (b%d!=0) printf("Impossible\n");
else
{
k=k*b/d;
t=t*b/d;
l=l/d;
if (k>=0)
k=k%l;
else k=k%l+l;
printf("%I64d\n",k);
}
}
return 0;
}
页:
[1]