算法——斐波那契数列问题

文章介绍了如何使用动态规划解决斐波那契数列问题,避免了递归带来的重复计算,从而提高了效率。通过优化,可以将空间复杂度降低到常量级别。此外,文章还扩展讨论了跳台阶问题和矩形覆盖问题,这两个问题同样可以用斐波那契数列的方法解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、斐波那契数列

题目: 牛客斐波那契队列题目

思路:
f ( x ) = { 1 x = 1 , 2 f ( x − 1 ) + f ( x − 2 ) x > 2 f(x) = \begin{cases} 1 &x=1,2 \\ f(x-1) + f(x-2) &x>2 \end{cases} f(x)={1f(x1)+f(x2)x=1,2x>2
第一想法:递归

存在问题:

  1. 计算 f ( x ) f(x) f(x)时要计算 f ( x − 1 ) f(x-1) f(x1) f ( x − 2 ) f(x-2) f(x2)
  2. 计算 f ( x − 1 ) f(x-1) f(x1)时,要计算 f ( x − 2 ) f(x-2) f(x2) f ( x − 3 ) f(x-3) f(x3)
  3. 以此类推,中间有无数个子问题需要重复计算

解决方法:动态规划

  1. 预设 f ( 1 ) f(1) f(1) f ( 2 ) f(2) f(2),从 f ( 3 ) f(3) f(3)开始计算
  2. 保留每个计算出的 f ( x ) f(x) f(x),这样每次计算,只需要使用之前的值

代码:

class Solution {
  public:
    int Fibonacci(int n) {
        if (n == 0)return 0;
        if (n <= 2) return 1;
        vector<int> befData;
        //保存f(1) f(2)
        befData.push_back(1);
        befData.push_back(1);
        for (int i = 2; i < n; i++) {
            befData.push_back(befData[(i - 1)] + befData[(i - 2)]);
        }
        return befData[befData.size() - 1];
    }
};

优化:只保留第 x x x项的前两项结果,也就是 f ( x − 1 ) f(x-1) f(x1) f ( x − 2 ) f(x-2) f(x2)

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

使用公式:
[ f ( n ) f ( n − 1 ) f ( n − 1 ) f ( n − 2 ) ] = [ 1 1 1 0 ] n − 1 {\left[ \begin{matrix} f(n) &f(n-1) \\ f(n-1) &f(n-2) \end{matrix} \right]} = {\left[ \begin{matrix} 1 &1 \\ 1 &0 \end{matrix} \right]}^{n-1} [f(n)f(n1)f(n1)f(n2)]=[1110]n1
斐波那契数列的扩展问题:跳台阶问题

二、跳台阶

题目: 牛客跳台阶题目扩展

分析:

青蛙跳到最后一阶台阶时,可以选择跳一步或者跳两步,也就是:
r e s [ n ] = { r e s [ n − 1 ] + 1 r e s [ n − 2 ] + 1 res[n] = {\begin{cases} res[n-1] + 1 \\ res[n-2] + 1 \end{cases}} res[n]={res[n1]+1res[n2]+1
所以,跳到最后一阶台阶的总跳法为跳到第 n − 1 n-1 n1个台阶的跳法加上跳到第 n − 2 n-2 n2个台阶的跳法,也就是:
f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n) = f(n-1) + f(n-2) f(n)=f(n1)+f(n2)
显而易见,斐波那契数列

代码:

#include <iostream>
using namespace std;

int f(int n) {
    if (n == 0) return 0;
    if (n <= 2) return 1;
    int n_1 = 1, n_2 = 1, res = 0;
    for (int i = 3; i <= n; i++) {
        res = n_1 + n_2;
        n_2 = n_1;
        n_1 = res;
    }
    return res;
}
int main() {
    int n = 0;
    scanf("%d", &n);
    printf("%d\n", f(n+1));
}

三、跳台阶扩展

题目: 牛客跳台阶扩展题目

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。

思路:跳最后一个台阶时,可以为倒数第二个台阶上跳一个台阶,也可以为倒数第三个台阶跳两个,也可以倒数第四个台阶跳三个,…,可以为第0个台阶处直接跳n个,也就是:
r e s [ n ] = { r e s [ n − 1 ] + 1 跳一个台阶 r e s [ n − 2 ] + 1 跳两个台阶 r e s [ n − 3 ] + 1 跳三个台阶 . . . r e s [ 0 ] + 1 跳 n 个台阶 res[n] = { \begin{cases} res[n-1] + 1 &跳一个台阶\\ res[n-2] + 1 &跳两个台阶\\ res[n-3] + 1 &跳三个台阶\\ ... \\ res[0] + 1 &跳n个台阶 \end{cases} } res[n]= res[n1]+1res[n2]+1res[n3]+1...res[0]+1跳一个台阶跳两个台阶跳三个台阶n个台阶
所以:
f ( n ) = ∑ n = 0 n − 1 f ( i ) f(n) = \sum_{n=0}^{n-1}{f(i)} f(n)=n=0n1f(i)
也就是说:
f ( n − 1 ) = ∑ n = 0 n − 2 f ( i ) f(n-1) =\sum_{n=0}^{n-2}{f(i)} f(n1)=n=0n2f(i)
则:
f ( n ) = f ( n − 1 ) + ∑ n = 0 n − 2 f ( i ) f(n)=f(n-1) +\sum_{n=0}^{n-2}{f(i)} f(n)=f(n1)+n=0n2f(i)

f ( n ) = 2 ∗ f ( n − 1 ) f(n) = 2*f(n-1) f(n)=2f(n1)

代码:

#include <iostream>
#include <vector>
using namespace std;

int f(int n) {
    int n_1 = 1;
    int res = 1;
    for(int i = 1; i < n;i++){
        n_1 = res;
        res = n_1 * 2;
    }
    return res;

}
int main() {
	int n = 0;
	scanf("%d", &n);
	printf("%d\n", f(n));
}
// 64 位输出请用 printf("%lld")

四、矩形覆盖问题

题目: 牛客矩形覆盖问题

分析:

假设 2 ∗ n 2*n 2n面积的矩形为 r e s [ n ] res[n] res[n],则,如果第一块矩形选择竖着覆盖, r e s [ n ] = r e s [ n − 1 ] + 1 res[n] = res[n-1] + 1 res[n]=res[n1]+1,如果第一块矩形选择横着覆盖,则, r e s [ n ] = r e s [ n − 2 ] + 1 res[n] = res[n-2] + 1 res[n]=res[n2]+1(每一次横着覆盖都必须是两个小矩形同时横着覆盖),也就是:
r e s [ n ] = { r e s [ n − 1 ] + 1 r e s [ n − 2 ] + 1 res[n] = \begin{cases} res[n-1] + 1 \\ res[n-2] + 1 \end{cases} res[n]={res[n1]+1res[n2]+1
所以:
f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n) = f(n-1) + f(n-2) f(n)=f(n1)+f(n2)
显而易见,还是个斐波那契数列问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值