动态规划之第 N 个泰波那契数/三步问题【leetCode】【算法】

动态规划

  如果问题是由重叠的子问题构成的,那就可以用动态规划(dynamic programming)来解决它。

  在求解动态规划问题的时候,我们需要思考以下5个步骤:

  1. 状态表示这是最重要的):我们会创建一个dp表,将较小问题的解放在表中,这样我们就会得到原始问题的解,所以状态表示就是清楚dp表里面某个位置所表示的含义。
  2. 状态转移方程最难的):也就是从题干中找到关于dp[i]的等式。
  3. 初始化:填表时,保证不越界。当求解问题时,需要知道较小问题的解,较小问题的解一定也是通过更小问题的解求得的,所以我们必须知道最初问题的解,以此来求得较大问题的解,这就需要我们限定dp[i]中i的取值范围。
  4. 填表顺序:当我们求解当前问题时,需要知道所需较小子问题的解,这就需要我们先求解得到较小子问题的解,这就是填表顺序。
  5. 返回值:题目要求+状态表示

  在代码中的体现为四个步骤:1. 创建dp表。 2. 初始化。 3. 填表。 4. 返回。

LeetCode题目

第 N 个泰波那契数

1137. 第 N 个泰波那契数
在这里插入图片描述

求解1

class Solution {
public:
    int tribonacci(int n) {

        // 处理边界问题
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;
        // 1. 创建dp数组
        vector<int> dp(n+1);
        // 2. 初始化
        dp[0] = 0, dp[1] = dp[2] = 1;
        // 3. 填表
        for(int i = 3; i <= n; i++)
        {
            dp[i] = dp[i-1]+dp[i-2]+dp[i-3];
        }
        // 4. 返回
        return dp[n];
    }
};

求解2(滚动数组)

  上面的求解1的空间复杂度时O(N)。
在这里插入图片描述
  通过上图我们容易看出来,每次求解的时候,我们只需要知道前面的三个值即可,但是求解1中我们使用了一个数组,这就浪费了我们得空间,我们优化就可以从这方面入手。
  定义四个变量,前三个变量表示dp[i-1], dp[i-2],dp[i-3]。第四个变量表示前三个变量相加的值,也就是dp[i]。每次需要求解下一个值的时候,就平移这前三个变量。

class Solution {
public:
    int tribonacci(int n) {
        // 1.创建dp表
        // 2.初始化
        int a = 0, b = 1, c = 1, d = 2;
        
        // 解决边界问题
        if(0 == n) return 0;
        if(1 == n || 2 == n) return 1;

        // 3.填表
        for(int i = 3; i <= n; i++)
        {
            d = a+b+c;
            a=b, b=c, c=d;
        }

        return d;
    }
};

三步问题

三步问题

在这里插入图片描述
  我们可以尝试手动求解前面几个的解,填入dp表。
在这里插入图片描述
  当我们计算到第4个台阶的时候,我们发现可以直接到达第4个台阶的方式分别是:

  1. 从第3个台阶起,上1个台阶到达。
  2. 从第2个台阶起,上2个台阶到达。
  3. 从第1个台阶起,上3个台阶到达。

  因为小孩一次只可以上1阶、2阶或3阶,所以只有这3种方式可以直接到达第4个台阶。
则我们经过第3个台阶到达第4个台阶的方式数有4种。
   经过第2个台阶到达第4个台阶的方式数有2种。
   经过第1个台阶到达第4个台阶的方式数有1种。
将三种方式相加,就是总的到达第4个台阶的方式数7种。
  按照这个方法往下求解,发现依旧适用。

于是简化理解,
       状态表示为:dp[i]表示到达第i个台阶的方式数量。
     状态转移方程为:dp[i] = dp[i-1]+dp[i-2]+dp[i-3];
        初始化为:dp[1] = 1, dp[2] = 2, dp[3] = 4;

求解1

class Solution {
public:
    int waysToStep(int n) {
        // 解决边界问题
        if(1 == n || 2 == n) return n;
        if(3 == n) return 4;
        
        // 1.创建dp表
        vector<int> dp(n+1);

        // 2. 初始化
        dp[1] = 1, dp[2] = 2, dp[3] = 4;
        
        // 3. 填表
        for(int i = 4; i <= n; i++)
        {
            dp[i] = ((dp[i-1]+dp[i-2])%1000000007+dp[i-3])%1000000007;
        }

        return dp[n] ;
    }
};

求解2(滚动数组)

class Solution {
public:
    int waysToStep(int n) {
        // 解决边界问题
        if(1 == n || 2 == n) return n;
        if(3 == n) return 4;
        
        // 1.创建dp表
        // 2. 初始化
        int a = 1, b = 2, c = 4, d = 0;
        
        // 3. 填表
        for(int i = 4; i <= n; i++)
        {
            d = ((a+b)%1000000007+c)%1000000007;
            a=b, b=c, c=d;
        }

        return d ;
    }
};

     😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😄

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七月巫山晴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值