HDU 2044 一只小蜜蜂... 斐波那契问题 2041 超级楼梯

老汉也即将面临找工作的抉择,为了不让自己死相太难看,现在打算刷刷题。都是比较粗浅,大神轻拍!

斐波那契定义

比萨的列奥纳多,又称斐波那契(Leonardo Pisano ,Fibonacci, Leonardo Bigollo,1175年-1250年),意大利数学家,西方第一个研究斐波那契数,并将现代书写数和乘数的位值表示法系统引入欧洲。

斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、……在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)

(这是老汉从百度上抄过来的,呵呵)

兔子问题

定义

斐波那契在《算盘书》中提出了一个有趣的兔子问题:
一般而言,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔都不死,那么一年以后可以繁殖多少对兔子?
我们不妨拿新出生的一对小兔子分析一下:
第一个月小兔子没有繁殖能力,所以还是一对;
两个月后,生下一对小兔总数共有两对;
三个月以后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对;
……
依次类推可以列出下表:

经过月数
0
1
2
3
4
5
6
7
8
9
10
11
12
幼仔对数
1
0
1
1
2
3
5
8
13
21
34
55
89
成兔对数
0
1
1
2
3
5
8
13
21
34
55
89
144
总体对数
1
1
2
3
5
8
13
21
34
55
89
144
233

可以明显看出,表中数字1,1,2,3,5,8---构成了一个斐波那锲序列
这时候,递推公式也需要灵活更改,F0 = 1, F1  = 1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)

实现

递归

所以,我们得到第一个代码--递归实现
#include <stdio.h>

#define MAX 50

long long Fib_recursion(int n)
{
	if (n == 0)
		return 0;
	else if(n == 1)
		return 1;
	else if(n > 1)
		return Fib_recursion(n - 1) + Fib_recursion (n - 2);
	else
		return -1;
}
int main(int argc, char *argv[])
{
	int n;
	long long result;

	while(scanf("%d", &n) != EOF)
	{
			
		result = Fib_recursion(n);
		printf("Done.After %d months, there are %lld pairs of rabbits!\n", n, result);
	}
	return 0;
}

现在我们对效率进行测试:

$ TIMEFORMAT="" time ./main
50
如上所示,我们采用命令time进行测试,在linux上使用TIMEFORMAT变量来重置默认的posix时间输出格式。得到如下结果:
Done.After 50 months, there are 20365011074 pairs of rabbits!

120.78user 0.00system 2:03.16elapsed 98%CPU (0avgtext+0avgdata 448maxresident)k
0inputs+0outputs (0major+153minor)pagefaults 0swaps
可以看出,当n=50时,这个程序需要占用98%的CPU,耗时两分钟才能跑完。效率太低了

复杂度分析

效率太慢,除了最后一个数,每个数都被算了一遍又一遍,时间复杂度差不多是5n^2/3

复杂度,引自 啊汉的博客


数组

现在我们采用数组实现斐波那契数列,代码如下
#include <stdio.h>

#define MAX 100


long long Fib_array(int n)
{
	long long a[MAX], result;
	int i;
	a[0] = 1;
	a[1] = 1;
	if(n < 2)
	{
		return -1;
	}
	for(i = 2; i <= n; i++)
	{
		a[i] = a[i - 1] + a[i - 2];
	}
	result = a[n];
	
	return result;

}
int main(int argc, char argv[])
{
	int n;
	long long result;

	scanf("%d", &n);
	
	result = Fib_array(n);
	printf("Done.After %d months, there are %lld pairs of rabbits!\n", n, result);

	return 0;
}


再次测试
$ TIMEFORMAT="" time ./main
50
瞬间,结果就出来了
Done.After 99 months, there are 3736710778780434371 pairs of rabbits!

0.00user 0.00system 0:01.71elapsed 0%CPU (0avgtext+0avgdata 452maxresident)k
0inputs+0outputs (0major+153minor)pagefaults 0swaps
可以看出,CPU占用率为0,时间为毫秒级

复杂度分析

空间复杂度和时间复杂度都是0(n),效率一般,比递归来得快。

复杂度,引自 啊汉的博客


迭代


#include <stdio.h>

long long Fib_iteration(int n)
{
	long long result, pre_result, temp;
	result = pre_result = 1;

	while(n >= 2)
	{
		temp = result;
		result += pre_result;
		pre_result = temp;
		n--;
	}
	return result;
}
int main(int argc, char argv[])
{
	int n;
	long long result;

	scanf("%d", &n);
	
	result = Fib_iteration(n);
	printf("Done.After %d months, there are %lld pairs of rabbits!\n", n, result);

	return 0;
}

复杂度分析

时间复杂度是o(n),空间复杂度是o(1)。效率最高

复杂度,引自 啊汉的博客




一只小蜜蜂

好了,说了半天了,还没进入正题。有关斐波那契问题,以后有机会再探讨。先进入HDOJ 2044题,看题



问题

点击打开2044


解题分析

看两个示例,来理解题目的意思

  1. a = 1 ,b = 2;有一种走法,1->2
  2. a = 1 , b = 3; 有两种走法, 1-2,1-3
  3. a = 1 , b = 4; 有三种走法,1-2-4, 1-3-4, 1-2-3-4
  4. a = 1, b = 5; 有五种走法 ,1-3-5,1-2-4-5,1-3-4-5,1-2-3-5,1-2-3-4-5

从上面的这几个例子中,可以发现这么个规律,前两次走法的和就是这一次的走法。老汉可以猜测这是个斐波那契数列;既然是斐波那契数列,那么如何确定n的值呢?

n = b - a?

看看这样行不行啊,

  1. a = 1 ,b = 2; n = 1; f(1) = 1;
  2. a = 1 , b = 3; n = 2; f(2) = 2;
  3. a = 1 , b = 4; n = 3; f(3) = 3 = f(1) + f(2)
  4. a = 1, b = 5; n =4; f(4) = 5 = f(2) + f(3)

 还真可以,行了,这就是个斐波那契数列了。有兴趣自己推一推 

代码

 

#include <stdio.h>

#define MAX 50

int main()
{
	/* gcc编译器,支持long long型变量 */
	long long fib[MAX];//windows编译器请写为__int64
	/* N为实例数量;a,b为两个蜂房编号;i,j作为循环变量*/
	int N, a, b, i, j;

	/*len 用来保存两个蜂房的距离,作为斐波那锲数列的参数*/
	int len;

	while (scanf("%d", &N) != EOF)
	{
		for(i = 0; i < N; i++)
		{
			scanf("%d %d", &a, &b);
			len = b - a;

			/*检查输入的正确性,如果a>b,报错*/
			if(a > b)
			{
				printf("error: Wrong Data Format!\n");
				break;
					
			}
			fib[1] = 1;
			fib[2] = 2;
			for(j = 3; j <= len; j++)
			{
				fib[j] = fib[j-1] + fib[j-2];
			}
			printf("%lld\n", fib[b-a]);
		}

	}
	return 0;
}

同类问题

HDOJ 2041 超级楼梯

题目

点击打开2041

分析

一画图,一看规律,一次加一或者加二,马上发现是一个典型的二阶Fibonacci(斐波那契)数列递增规律:

每加一格楼梯就会增加两条新路线,一条连向前一格,一条连向前两格,即递增规律Fib[i]=Fib[i-1]+Fib[i-2];

代码

#include <stdio.h>


int main(int argc, char *argv[])
{
	int n, m;
        /*windows环境下请替换为__int64型*/
        long long result, pre_result, temp;

	scanf("%d", &n);
	while(n--)
	{
		scanf("%d", &m);
		result = pre_result = 1;
		while(m > 2)
		{
			temp = result;
			result += pre_result;
			pre_result = temp;
			m--;
		}
                /*windows环境下请替换为%I64d*/
                printf("%lld\n", result);
    }
         return 0;
}


                       

     
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值