代码随想录算法训练营Day28 | 509.斐波那契数列、70.爬楼梯、746.使用最小花费爬楼梯

LeetCode509.斐波那契数列

509. 斐波那契数 - 力扣(LeetCode)

1.思路

动态规划

斐波那契数列这道题算是最简单的动态规划的题了。接下来通过这道题来掌握

动态规划五部曲:

1.确定dp数组及下标的含义:dp[i] -> 第i个斐波那契数的值;

2.确定递推公式:状态转移方程 -> dp[i] = dp[i - 1] + dp[i - 2] ;

3.dp数组如何初始化:dp[0]=0,dp[1]=1;

4.确定遍历顺序:从状态转移方程可知 dp[i] 依赖于 dp[i-1] 和 dp[i-2],所以遍历顺序一定是从前向后的;

5.举例推导dp数组:

当N为10的时候,dp数组应该是这样的数列: 0 1 1 2 3 5 8 13 21 34 55 ,如果代码写出来,发现结果不对,就把dp数组打印出来看看和我们推导的数列是不是一致的。

cpp 复制代码
class Solution {
public:
    int fib(int n) {
        if(n<=1) return n;
        vector<int>dp(n+1);
        dp[0]=0;
        dp[1]=1;
        for(int i=2;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
};
优化

这里其实只需要维护两个数值,所以不需要记录整个序列,就只需要定义两个变量并记录最后的值即可。

cpp 复制代码
class Solution {
public:
    int fib(int n) {
        if(n<=1) return n;
        int dp[2];
        dp[0]=0;
        dp[1]=1;
        for(int i=2;i<=n;i++){
            int sum=dp[0]+dp[1];
            dp[0]=dp[1];
            dp[1]=sum;
        }
        return dp[1];
    }
};
递归

递归的方法是最常见的。

cpp 复制代码
class Solution {
public:
    int fib(int n) {
        if(n<=1) return n;
        return fib(n-1)+fib(n-2);
    }
};

2.复杂度分析

时间复杂度:O(n) - 动规、优化,O(2^n) - 递归

空间复杂度:O(n) - 动规、递归,O(1) - 优化

3.思考

这道题虽然很简单,但确是掌握动态规划的入门第一步,我们要从中熟悉并掌握动态规划题目的解题方法思想。

4.Reference:509. 斐波那契数 | 代码随想录


LeetCode70.爬楼梯

70. 爬楼梯 - 力扣(LeetCode)

1.思路

这道题需要多举几个例子,例如爬到第一层楼梯有一种方法,爬到第二层有两种方法,爬到第三次有三种方法,依次类推。可以得知,爬到第三层的方法由爬到第二层和爬到第一层的方法推导出来。

动态规划五部曲:

  1. dp[i]:爬到第 i 层楼梯有 dp[i] 种方法;

  2. 要到达 dp[i] ,可以从 dp[i-1] 处跳一步,也可以从 dp[i-2] 处跳两步,由此可知,dp[i] = dp[i-1] + dp[i-2];

  3. 爬到第一层有 1 种方法,爬到第二层有 2 种方法,所以 dp[1] = 1,dp[2] = 2 。这里不需要从 0 开始初始化,因为爬到第 0 层的方法为 0 ,而且 dp[i] 表示的是爬到第 i 层的方法数,如果从 0 开始初始化的话意义就不是那么明显,并且题目说了 n 是一个正整数,所以从 0 开始初始化压根就没意义;

  4. 由递归公式可知,dp[i] 依赖于 dp[i-1]、dp[i-2],所以从前往后遍历;

  5. 依旧举例比较,当 n 为 5 的时候,dp数组应该是这样的数列:1 2 3 5 8 ;

cpp 复制代码
// 版本一
class Solution {
public:
    int climbStairs(int n) {
        if (n <= 1) return n; 
        vector<int> dp(n + 1);
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= n; i++) { // 注意i是从3开始的
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
};
cpp 复制代码
// 版本二
class Solution {
public:
    int climbStairs(int n) {
        if (n <= 1) return n;
        int dp[3];
        dp[1] = 1;
        dp[2] = 2;
        for (int i = 3; i <= n; i++) {
            int sum = dp[1] + dp[2];
            dp[1] = dp[2];
            dp[2] = sum;
        }
        return dp[2];
    }
};

2.复杂度分析

时间复杂度:O(n)

空间复杂度:O(n) - 方法一,O(1) - 方法二

3.思考

这道题和斐波那契数列那道题的递推公式是一样的,但是如果是第一次做还是有难度的,因为这里的递推公式得自己去推,dp数组的初始化也得思考一下是否有意义。

4.Reference:70. 爬楼梯 | 代码随想录


LeetCode746.使用最小花费爬楼梯

746. 使用最小花费爬楼梯 - 力扣(LeetCode)

1.思路

题目说的可以自行选择下标 0 或下标 1 的台阶开始爬楼梯,也就相当于跳到下标 0 或 1 是不消耗体力的。

动态规划五部曲:

  1. dp[i]:到达第 i 个台阶所需要的最小体力;

  2. dp[i] 可由 dp[i-1] 跳到 dp[i],此时花费 dp[i-1]+cost[i-1];也可以由 dp[i-2] 跳到 dp[i],此时花费 dp[i-2] + cost[i-2],由于 dp[i] 是到第 i 个台阶的最小体力,所以 dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]) ;

  3. 由题目可知,跳到下标 0 或下标 1 是不消化体力的,所以可以初始化 dp[0]=0,dp[1]=0;

  4. 由递推公式可知是从前往后遍历的;

  5. 拿示例2:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] ,dp = [0,0,1,2,2,3,3,4,4,5,6] ;

cpp 复制代码
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        vector<int>dp(cost.size()+1);
        dp[0]=0;
        dp[1]=0;
        for(int i=2;i<=cost.size();i++){
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[cost.size()];
    }
};

优化

cpp 复制代码
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int dp0=0;
        int dp1=0;
        for(int i=2;i<=cost.size();i++){
            int sum=min(dp0+cost[i-2],dp1+cost[i-1]);
            dp0=dp1;
            dp1=sum;
        }
        return dp1;
    }
};

2.复杂度分析

时间复杂度:O(n)

空间复杂度:O(n),O(1) - 优化

3.思考

这道题较前两到题就上难度了,因为不是单纯的由前两个 dp 数组的状态相加得到的,这里还需要加上从当前位置起跳的花费 cost[i],最终还要存最小值,所以状态转移方程就很难想到;其次是 dp 数组的初始化,这里必须要读懂题目,可以任意选择从 0 或 1 位置开始走,就说明跳到 0 或 1 是不消耗体力的。

4.Reference:746. 使用最小花费爬楼梯 | 代码随想录

相关推荐
Boop_wu1 小时前
[Java EE] 多线程进阶(JUC)(2)
java·jvm·算法
闻缺陷则喜何志丹2 小时前
【SOSDP模板 容斥原理 逆向思考】3757. 有效子序列的数量|分数未知
c++·算法·力扣·容斥原理·sosdp·逆向思考
CoovallyAIHub2 小时前
如何在手机上轻松识别多种鸟类?我们发现了更简单的秘密……
深度学习·算法·计算机视觉
第二只羽毛2 小时前
遵守robots协议的友好爬虫
大数据·爬虫·python·算法·网络爬虫
艾斯比的日常2 小时前
Java 三色标记算法:并发垃圾回收的核心技术解析
java·开发语言·算法
CoovallyAIHub2 小时前
抛弃LLM!MIT用纯视觉方法破解ARC难题,性能接近人类水平
深度学习·算法·计算机视觉
程序猿编码2 小时前
PRINCE算法的密码生成器:原理与设计思路(C/C++代码实现)
c语言·网络·c++·算法·安全·prince
高洁013 小时前
具身智能-视觉语言导航(VLN)
深度学习·算法·aigc·transformer·知识图谱
Croa-vo3 小时前
TikTok 数据工程师三轮 VO 超详细面经:技术深挖 + 建模推导 + 压力测试全记录
javascript·数据结构·经验分享·算法·面试