ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NYOJ 314 解题报告

2021-07-11 12:01:56  阅读:163  来源: 互联网

标签:20 gcd Extend int NYOJ long 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有