跳台阶问题|斐波那契递归的复杂度问题|整数划分问题

本文探讨了不同跳跃步数的台阶问题,并提供了多种算法解决方案,包括递归、递推和矩阵快速幂方法,对比了它们的时间和空间复杂度。

【问题】

台阶一共有N节,一次可以跳1节或者2节,问有多少次跳法?如果一次可以跳1节、2节或者3节呢?

【分析】

定义f(N)为第几节台阶时候的跳法,那么,
  1. N=1时,f(N)=1; 
  2. N=2时, f(N)=2;因为有每次跳一步和一次跳两步两种
  3. 当N>2时,如果第一次跳1阶,那么要跳到第N节有,需要将剩下的N-1节跳完,f(N-1);如果第一次跳2阶,那么需要将剩下的N-2节跳完,有f(N-2)种跳法,两种起始跳法互斥,所以由f(N)=f(N-1)+f(N-2)

【代码】

long long fibonacci_soluction(int N)
{

	if(N <=2)
		return N;
	else
		return fibonacci_soluction(N-1)+fibonacci_soluction(N-2);

}

同理,若是一次可以跳1阶、2阶或者3阶,那么
  1. f(N)=1 N=1
  2. f(N)=2 N=2
  3. f(N)=4 N=3
  4. f(N)=f(N-1)+f(N-2)+f(N-3) n>3
代码如下:
long long fibonacci_soluction2(int N)
{
	if(N == 1)
		return 1;
	if(N == 2)
		return 2;
	if(N == 3)
		return 4;
	else
		return fibonacci_soluction2(N-1)+fibonacci_soluction2(N-2)+fibonacci_soluction2(N-3);
}


【思考】菲波那切数列时间空间复杂度

转化成斐波那契数列递归求解,貌似是一个漂亮的解法,但是时间O(2^N)和空间复杂度O(N)不敢恭维,改用递推,时间复杂度为O(N),空间复杂度为O(1)
long long fibonacci_soluction_rec(int N)
{

	if(N <= 2)
		return N;
	long long a, b, c;
	a = 1, b = 2;
	int i;
	for (int i = 3; i <= N; i++)
	{
		c = a + b;
		a = b;
		b = c;
	}	
	return c;

}

但这是实现菲波那切数列最优的方法么?参考 http://www.gocalf.com/blog/calc-fibonacci.html
答案:矩阵法是最快的算法,具体推导见上面的链接用python实现的,用C++实现的http://zhedahht.blog.163.com/blog/static/25411174200722991933440/
最终的代码如下:
/*  f[n]       1*f[n-1] + 1* f[n-2]    1  1    f[N-1]   | 1  1 |^(N-1)    f[1]
-----  =   ---------------      =       *         =*
f[n-1]   1*f[n-1] + 0* f[n-1]   1  0  f[N-2]   | 1  0 |  f[0]
转变为求1  1 二维矩阵的N-1次幂的形式
1  0 
/  an/2*an/2                      n为偶数时
an=
\  a(n-1)/2*a(n-1)/2            n为奇数时
这样只需logN次就行了
*/
struct matrix_2by2
{
	matrix_2by2(long long a_00 = 0,	long long a_01 = 0,	long long a_10 = 0, long long a_11 = 0)
	:a00(a_00),a01(a_01),a10(a_10),a11(a_11)//为书写方便,给struct一个构造函数
	{

	}
	long long a00;
	long long a01;
	long long a10;
	long long a11;	
};
typedef struct matrix_2by2 matrix_2by2;
matrix_2by2 matrix_2by2_multiple(const matrix_2by2 A, const matrix_2by2 B)
{
	return matrix_2by2
	(
		A.a00*B.a00 + A.a01*B.a10,
		A.a00*B.a01 + A.a01*B.a11,
		A.a10*B.a00 + A.a11*B.a10,
		A.a10*B.a01 + A.a11*B.a11	
	);

}
matrix_2by2 matrix_pow(int N)
{
	assert(N > 0);
	matrix_2by2 matrix;
	if (N == 1)
	{
		return matrix_2by2(1, 1, 1, 0);
	}
	if (N % 2 == 0)
		matrix = matrix_2by2_multiple(matrix_pow(N/2), matrix_pow(N/2));

	if (N % 2 == 1)
	{
		matrix = matrix_2by2_multiple(matrix_pow((N-1)/2), matrix_pow((N-1)/2));
		matrix = matrix_2by2_multiple(matrix, matrix_2by2(1,1,1,0));
	}
	return matrix;
}

long long matrix_fabonacci(int N)
{
	if(N <=2)
		return N;
	matrix_2by2 res = matrix_pow(N);
	return res.a00;	
}

测试代码

#include <time.h>
#include <iostream>
using namespace std;
int main()
{
	
	time_t begin, end;
	begin = clock();
	fibonacci_soluction_rec(10000000);
	end = clock();
	cout<<"使用递推fibonacci(100)用时"<<end-begin<<endl;
	begin = clock();
	matrix_fabonacci(10000000);
	end = clock();
	cout<<"使用矩阵fibonacci(100)用时"<<end-begin<<endl;

}
不知为什么,我测试的矩阵方法竟然比递归的方法慢了50多倍!有时间在考虑下~~

【思考】当跳的步数不止三个呢,这就转化为整数规划的问题

long long equationCount(int n, int m)
{
	assert(m >0 && n>0);
	if (m==1 || n==1)
		return 1;
	if (n <= m)
		return 1+equationCount(n, n-1);
	if (n > m)
		return equationCount(n-m, m)+equationCount(n, m-1);

}

当然,对上述递归算法实际中需要进行优化。(完,断断续续写了三天=_=)

<think>根据引用内容,青蛙跳台问题是一个经典的递归问题,其规律与斐波那契数列类似。问题描述如下: - 一只青蛙一次可以1台阶,也可以2台阶(有些问题中还可以3级,但这里以最常见的两种法为例)。 - 求该青蛙上一个n级的台阶总共有多少种法。 通过引用[3]和引用[5]的分析,我们可以得到: - 当n=1时,只有1法(1级)。 - 当n=2时,有2法(一次2级,或分两次每次1级)。 - 当n>2时,青蛙的最后一步有两种选择1级或2级。如果最后一步1级,则前面的法为f(n-1);如果最后一步2级,则前面的法为f(n-2)。因此,总法数为f(n) = f(n-1) + f(n-2)。 这实际上就是斐波那契数列的递推关系,只是初始值不同: - 斐波那契数列:F(0)=0, F(1)=1, F(2)=1, F(3)=2, ... - 青蛙跳台12级):f(1)=1, f(2)=2, f(3)=3, f(4)=5, ... 即从f(1)开始就是斐波那契数列的移位。 因此,我们可以用递归方法实现,但需要注意递归的终止条件。 C++递归实现代码: ```cpp #include <iostream> int jumpWays(int n) { if (n <= 0) return 0; // 没有台阶法为0 if (n == 1) return 1; // 1台阶只有1法 if (n == 2) return 2; // 2台阶2法 return jumpWays(n-1) + jumpWays(n-2); // 对于n>2 } int main() { int n; std::cout << "请输入台阶数: "; std::cin >> n; std::cout << "法总数: " << jumpWays(n) << std::endl; return 0; } ``` ### 代码说明: 1. **递归终止条件**: - $n \leq 0$:返回0,因为台阶数非正时没有法。 - $n=1$:返回1(只有一种法:1级)。 - $n=2$:返回2(两种法:连续两次1级,或一次2级)。 2. **递归关系**: - 当 $n>2$ 时,法数等于上 $n-1$ 级台阶法数加上上 $n-2$ 级台阶法数,即: $$f(n) = f(n-1) + f(n-2)$$ 3. **示例**: - 输入 $n=3$,输出 $3$(法:1+1+1, 1+2, 2+1)。 - 输入 $n=4$,输出 $5$(法:1+1+1+1, 1+1+2, 1+2+1, 2+1+1, 2+2)。 ### 注意事项: 1. **时间复杂度**:$O(2^n)$,指数级,效率较低。当 $n$ 较大时(如 $n>40$)会非常慢。 2. **重复计算问题**:递归过程中会重复计算很多子问题(如计算 $f(5)$ 时需要计算 $f(4)$ 和 $f(3)$,而计算 $f(4)$ 时又会计算 $f(3)$ 和 $f(2)$)。 3. **栈溢出风险**:递归深度过大可能导致栈溢出。 ### 优化建议: 1. **记忆化递归**:使用数组或哈希表存储已计算的子问题结果,避免重复计算,将时间复杂度降为 $O(n)$。 2. **动态规划**:使用循环自底向上计算,避免递归开销(见引用[4])。 > 如果青蛙一次可以1级、2级或3台阶(如引用[2]所述),则递推关系变为 $f(n) = f(n-1) + f(n-2) + f(n-3)$,终止条件需增加 $n=3$ 的情况($f(3)=4$)[^2]。 §§相关问题§§ 1. 如何用动态规划优化青蛙跳台问题2. 青蛙跳台问题的时间复杂度如何分析? 3. 青蛙跳台问题斐波那契数列有什么联系和区别? 4. 如果青蛙一次可以任意1~n级),法总数是多少? 5. 记忆化递归在C++中如何实现? [^1]: 青蛙跳台问题的递归关系与斐波那契数列类似。 [^2]: 当青蛙一次可以123级时,递推关系变为三项和。</think>### C++递归实现青蛙跳台问题 青蛙跳台问题是一个经典的递归问题,其核心思想是通过分解问题为更小的子问题来解决。根据台阶数的不同,青蛙的法遵循特定的规律: #### 问题分析 1. **台阶数与法关系**: - 当 $n=1$ 时:只有1法(直接1级)[^3] - 当 $n=2$ 时:有2法(1+1 或 直接2级)[^3] - 当 $n=3$ 时:有3法(1+1+1, 1+2, 2+1)[^1] - 当 $n>3$ 时:法数 = $n-1$ 级的方法 + $n-2$ 级的方法[^5] 2. **递推公式**: $$f(n) = \begin{cases} 1 & n=1 \\ 2 & n=2 \\ f(n-1) + f(n-2) & n>2 \end{cases}$$ #### C++递归实现代码 ```cpp #include <iostream> int frogJump(int n) { // 基础情况处理 if (n <= 0) return 0; // 无效台阶数 if (n == 1) return 1; // 只有1法 [^3] if (n == 2) return 2; // 有2法 [^3] // 递归关系:f(n) = f(n-1) + f(n-2) [^5] return frogJump(n - 1) + frogJump(n - 2); } int main() { int steps; std::cout << "请输入台阶数: "; std::cin >> steps; std::cout << "法总数: " << frogJump(steps) << std::endl; return 0; } ``` #### 代码说明 1. **递归终止条件**: - $n=1$:返回1(直接1级) - $n=2$:返回21+1 或 直接2级) 2. **递归关系**: $$f(n) = \text{frogJump}(n-1) + \text{frogJump}(n-2)$$ 表示最后一步可以选择1级(剩余 $n-1$ 级)或2级(剩余 $n-2$ 级)[^5] 3. **示例输出**: ```bash 请输入台阶数: 3 法总数: 3 # (1+1+1, 1+2, 2+1) ``` #### 复杂度与优化 1. **时间复杂度**:$O(2^n)$,存在大量重复计算 2. **优化建议**: - 使用记忆化存储(缓存已计算结果) - 改用动态规划(时间复杂度 $O(n)$)[^4] - 对于能3级的情况,递推式改为 $f(n) = f(n-1) + f(n-2) + f(n-3)$[^2] > 此实现直观展示了递归思想,但实际应用中应考虑优化方案以避免指数级时间复杂度[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值