代码随想录算法训练营第三十八天 | 509. 斐波那契数70. 爬楼梯746. 使用最小花费爬楼梯

Posted piolet0016

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码随想录算法训练营第三十八天 | 509. 斐波那契数70. 爬楼梯746. 使用最小花费爬楼梯相关的知识,希望对你有一定的参考价值。

前期基础:
(一)关键五步

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

509. 斐波那契数

视频讲解

主要思路:

1.确定dp数组以及下标的含义

dp[i]的定义为:第i个数的斐波那契数值是dp[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];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的

5.举例推导dp数组

按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列:

0 1 1 2 3 5 8 13 21 34 55

代码实现:

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];
    
;

第一次写错误 

还有可能出现只有一位的情况

70. 爬楼梯

视频讲解

主要思路:

与上一题菲波那切数列基本一致,关键在于发现递推公式

1.确定dp数组以及下标的含义

dp[i]: 爬到第i层楼梯,有dp[i]种方法

2.确定递推公式

如何可以推出dp[i]呢?

从dp[i]的定义可以看出,dp[i] 可以有两个方向推出来。

首先是dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,那么再一步跳一个台阶不就是dp[i]了么。

还有就是dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,那么再一步跳两个台阶不就是dp[i]了么。

那么dp[i]就是 dp[i - 1]与dp[i - 2]之和!

所以dp[i] = dp[i - 1] + dp[i - 2] 。

在推导dp[i]的时候,一定要时刻想着dp[i]的定义,否则容易跑偏。

这体现出确定dp数组以及下标的含义的重要性!

3.dp数组如何初始化

在回顾一下dp[i]的定义:爬到第i层楼梯,有dp[i]中方法。

那么i为0,dp[i]应该是多少呢,这个可以有很多解释,但基本都是直接奔着答案去解释的。

例如强行安慰自己爬到第0层,也有一种方法,什么都不做也就是一种方法即:dp[0] = 1,相当于直接站在楼顶。

但总有点牵强的成分。

那还这么理解呢:我就认为跑到第0层,方法就是0啊,一步只能走一个台阶或者两个台阶,然而楼层是0,直接站楼顶上了,就是不用方法,dp[0]就应该是0.

其实这么争论下去没有意义,大部分解释说dp[0]应该为1的理由其实是因为dp[0]=1的话在递推的过程中i从2开始遍历本题就能过,然后就往结果上靠去解释dp[0] = 1

从dp数组定义的角度上来说,dp[0] = 0 也能说得通。

需要注意的是:题目中说了n是一个正整数,题目根本就没说n有为0的情况。

所以本题其实就不应该讨论dp[0]的初始化!

所以:不考虑dp[0]如何初始化,只初始化dp[1] = 1,dp[2] = 2,然后从i = 3开始递推,这样才符合dp[i]的定义。

4.确定遍历顺序

从递推公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,遍历顺序一定是从前向后遍历的

5.举例推导dp数组

代码实现:

class Solution 
public:
    int climbStairs(int n) 
        if(n <= 2) return n;
        int dp[2];  //dp[i]表示爬到第i阶台阶有几种方法,不过这里与斐波那契数列一样,只用维护两个就行
        dp[0] = 1;  
        dp[1] = 2;
        for(int i = 3; i <= n; i++) 
            int sum = dp[0] + dp[1];
            dp[0] = dp[1];
            dp[1] = sum;
        
        return dp[1];
    
;

746. 使用最小花费爬楼梯

视频讲解

主要思路:

(1)dp数组及下标含义:跳到第i个楼梯所需最小体力值

(2)递推公式:因为一次能跳1或2个台阶,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]

(3)初始化:可以从第0个或第1个台阶开始跳,所以初始化dp[0]与dp[1]为0

(4)遍历顺序:从前往后

易错点:

(1)i表示台阶序号,最后顶楼的序号是cost.size()

代码实现:

class Solution 
public:
    int minCostClimbingStairs(vector<int>& cost) 
        if(cost.size() <= 1) return 0;
        int dp[2];  //dp数组中记录的是跳到当前位置所需的最小体力值
        //空间上的优化,因为只用记录要跳到当前位置前两个位置即可
        dp[0] = 0;  
        dp[1] = 0;
        for(int i = 2; i <= cost.size(); i++)  //i <= cost.size()是因为i表示跳到i这个位置,最后的顶楼位置下标也就是cost数组长度
            int next = min(dp[0] +cost[i - 2], dp[1] + cost[i - 1]);
            dp[0] = dp[1];
            dp[1] = next;
        
        return dp[1];
    
;

第一次写错误 

(1) cost数组大小为1时才直接返回0

代码随想录算法训练营第五十八天|739.每日温度496.下一个更大元素Ⅰ

day58 2023/03/30

一、每日温度

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

分析如下:

要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了

单调栈的本质是空间换时间

在使用单调栈的时候首先要明确如下几点:

1.单调栈里存放的元素是什么?

单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。

2.单调栈里元素是递增呢? 还是递减呢?

注意以下讲解中,顺序的描述为 从栈头到栈底的顺序

使用单调栈主要有三个判断条件。

  • 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

具体分析见代码随想录,分析的好透彻,好强 

代码如下:

class Solution 
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) 
     stack<int> st;
     vector<int> result(temperatures.size(),0);
     st.push(0);
     for(int i=1;i<temperatures.size();i++)
     
         if(temperatures[i]<temperatures[st.top()])
           st.push(i);
         else if(temperatures[i]==temperatures[st.top()])
           st.push(i);
        else
        
            while(!st.empty()&&temperatures[i]>temperatures[st.top()])
            
               result[st.top()]=i-st.top();
               st.pop();
            
            st.push(i);
        
     
     return result;
    
;

二、下一个更大元素Ⅰ

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

分析如下:

整体逻辑与上题相同,只是本题使用了一个map

代码如下:

class Solution 
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) 
        stack<int> st;
        vector<int> result(nums1.size(), -1);
        if (nums1.size() == 0) return result;

        unordered_map<int, int> umap; // key:下标元素,value:下标
        for (int i = 0; i < nums1.size(); i++) 
            umap[nums1[i]] = i;
        
        st.push(0);
        for (int i = 1; i < nums2.size(); i++) 
            if (nums2[i] < nums2[st.top()])            // 情况一
                st.push(i);
             else if (nums2[i] == nums2[st.top()])    // 情况二
                st.push(i);
             else                                     // 情况三
                while (!st.empty() && nums2[i] > nums2[st.top()]) 
                    if (umap.count(nums2[st.top()]) > 0)  // 看map里是否存在这个元素
                        int index = umap[nums2[st.top()]]; // 根据map找到nums2[st.top()] 在 nums1中的下标
                        result[index] = nums2[i];
                    
                    st.pop();
                
                st.push(i);
            
        
        return result;
    
;

以上是关于代码随想录算法训练营第三十八天 | 509. 斐波那契数70. 爬楼梯746. 使用最小花费爬楼梯的主要内容,如果未能解决你的问题,请参考以下文章

代码随想录算法训练营第五十八天|739.每日温度496.下一个更大元素Ⅰ

代码随想录算法训练营第四十八天 | 198.打家劫舍 213.打家劫舍II337.打家劫舍III

第三十八天

走入计算机的第三十八天(python的进程和协成))

爱创课堂每日一题第三十八天-web storage和cookie的区别?

代码随想录|day38|动态规划part01● 理论基础 ● 509. 斐波那契数 ● 70. 爬楼梯 ● 746. 使用最小花费爬楼梯