其他分享
首页 > 其他分享> > NYOJ 314 解题报告

NYOJ 314 解题报告

作者:互联网

斐波那契数列四吧

时间限制: 3000 ms  |  内存限制: 65535 KB 难度: 2 描述

斐波那契数列为:0,1,1,2,3,5,8,13....,常规递推公式为f(n)=f(n-1)+f(n-2);

但这里不一样的是,我们的前两个数字不一定是0,1;

也就是说,斐波那契数列的前两个数字是随机数(保证是非负整数)。下面我们开始吧,我会告诉你斐波那契数列的第20项的值m,请你判断是否会有这样的数列存在。

对了,你要保证数列的每一项都是整数哦

输入 第一行呢,按照惯例我们输入一个数字N,表示测试数据组数。
接下来肯定就有N行了。。
每行包含一个整数m(m<10000000),表示数列的第20项的值。 输出 如果存在这样的数列呢,输出该数列的第一项和第二项的值
else 输出“No answer”
每组数据输出后换行。
样例输入
1
1000000
样例输出
154 144

       这里有一点需要注意,f(1)=1而非题目中的那个0,因为按题目的意思,f(0)=0。

       首先根据斐波那契数列的递推公式从F(20)开始倒推,F(20)=F(19)+F(18),而F(19)=F(18)+F(17),F(18)=F(17)+F(16),得出F(20)=3F(17)+2F(16),然后一直迭代下去:F(20)=8F(15)+5F(14), F(20)=21F(13)+13F(12),F(20)=55F(11)+34F(10),F(20)=144F(9)+89F(8),F(20)=377F(7)+233F(6),F(20)=987F(5)+610F(4),F(20)=2584F(3)+1597F(2),有没有发现前面的系数也是斐波那契数列的项呢?最后一步,由F(3)=F(2)+F(1)得F(20)=4181F(2)+2584F(1)。这个式子能够帮助我们由第20项推出第1、2两项。

       所以,这道题归根结底就是求解丢番图方程1597x+2584y=f(20)的最小正整数解。这里,gcd(1597, 2584)=1一定能整除f(20),即1 | f(20)。我是这样思考的,首先根据扩展的欧几里得算法求得这个方程的一组特解。

       扩展的欧几里得算法是这样的,找到一组整数s, t使得a*s+b*t=c。实现它是使用一个递归。递归的终止条件是gcd(a,b)*1+b*0=gcd(a,b)。因此需要不停地使用欧几里得算法的步骤来找出s和t前面的一系列系数。先看一下代码:

long long Extend_Euclid(long long *s,long long *t,long long a,long long b)
{
	if(b==0)
	{
		*s=1;
		*t=0;
		return a;
	}
	long long ans=Extend_Euclid(s,t,b,a%b);
	long long temp=(*s);
	*s=*t;
	*t=temp-(a/b)*(*t);
	return ans;
}

        函数的返回值是s前面的系数。

        而后面的几句赋值语句的意思是,将后一步得出的t值赋值给s,再将后一步得出的s和t得到的s-[a/b]*t赋值个t。这是为什么呢?因为gcd(a, b)=s*rj+t*r(j-1),而欧几里得算法里rj=r(j-2)-r(j-1)*q(j-1),代入有gcd(a, b)=(t-s*q(j-1))*r(j-1)+s*r(j-2)。最后r0=a,r1=b。

        但是现在得到的s和t值又可能是有负数的,所以还要根据通解x=x0+(b/gcd(a, b))*n,y=y0-(a/gcd(a, b))*n一步步寻找使得x, y均大于0的一组解,而且这个一定是最小的正整数解。如果找不到,就表明No Answer。

        最终代码如下:

#include <stdio.h>

long long Extend_Euclid(long long *s,long long *t,long long a,long long b)
{
	if(b==0)
	{
		*s=1;
		*t=0;
		return a;
	}
	long long ans=Extend_Euclid(s,t,b,a%b);
	long long temp=(*s);
	*s=*t;
	*t=temp-(a/b)*(*t);
	return ans;
}

int main()
{
	int N;
	scanf("%d",&N);
	long long s,t;
	Extend_Euclid(&s,&t,2584,4181);
	while(N--)
	{
		long long m,x,y;
		int flag=0;
		scanf("%lld",&m);
		x=m*s,y=m*t;
		while(x<0||y<0)
        {
            if(x<0)
            {
                x=x+4181;
                y=y-2584;
                if(x>=0&&y<0)
                {
                    flag=1;
                    printf("No answer\n");
                    break;
                }
            }
            else
            {
                x=x-4181;
                y=y+2584;
                if(y>=0&&x<0)
                {
                    flag=1;
                    printf("No answer\n");
                    break;
                }
            }
        }
        if(flag==0)
            printf("%lld %lld\n",x,y);
	}
	return 0;
}

         再来看看标程,简单粗暴,直接上枚举。而且在某些情况下,直接枚举比我的程序运行时间更短!

 
#include <stdio.h>
int main()
{
	int m,n;
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d",&n);int flag=0;
		for(int i=0;i*4181<=n;++i)
		{
			if((n-i*4181)%2584==0)  {printf("%d %d\n",(n-i*4181)/2584,i);flag=1;break;}
		}
		if(flag==0)  printf("No answer\n");
	}
	return 0;
}        




标签:20,gcd,Extend,int,NYOJ,long,314,解题,数列
来源: https://blog.51cto.com/liulizhi1996/3035757