代码随想录Day42

今天开始正式进入动态规划部分,不过动态规划在我开始写博客之前已经做过一部分,但当时归当时,现在应当重新以更加细致的方式学习动规。

509.斐波那契数

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。

示例 1:

输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

思路:

1.显然在题目和示例中都已经告诉了我们dp数组的公式,dp[i] = dp[i - 1] + dp[i - 2],然后初始化dp[0] = 1, dp[1] = 1。

2.但实际上本题可以进一步优化,因为公式中涉及到的实际上都是三个常数,因此我们可以只使用三个整型变量进行不断地迭代就可。

class Solution {
public:
    int fib(int n) {
         int a = 0, b = 1 , c = 1;
         for(int i = 0; i < n; i++){
             int sum = b + c;
             a = b;
             b = c;
             c = sum;
         }

         return a;
    }
};

启发:

1.本题初次接触可能都会想到使用递归的方法,但递归在n较大时无论是时间复杂度还是因为递归函数调用的栈的空间都会比较大。通过题目已经给出的公式可以想到通过动态规划进行优化。

2.另一个优化的点在于dp数组,因为我们实际上只要找到第n个数就可以了,我们没有必要使用额外的空间来记录第n个数之前的所有数字,但要推导出第n个数又不得不需要前面的所有数字的帮助,因此我们可以用三个整型变量来代替公式中的三个数,进行不断的迭代就可以了。

需要注意的是最后返回的变量时a,a在一开始初始化为0就是因为题目中给出的n的取值范围是可以取0的,因此a初始化为0代表第0个斐波那契数字,迭代完毕后自然a代表的就是第n为斐波那契数字。

70.爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

思路:

1.本题与斐波那契数列是同样的思路,但因为涉及到一个爬楼梯的场景,所以需要时刻提醒自己:要求的是方法数,不是跳了几次

class Solution {
public:
    int climbStairs(int n) {
        long long a = 0, b = 1, c = 2;
        for(int i = 0; i < n; i++){
            long long sum = b + c;
            a = b;
            b = c;
            c = sum;
        }
        return a;
    }
};

启发:

1.本题尽管代码与上一道题基本一致,但还是有需要注意的点,首先是本题的初始化,dp[1] = 1, dp[2] = 2,或许会疑惑,dp[0]到底该初始化为多少?尽管题目中n>=1,但还是会想要考虑一下这点。

但实际上纠结于dp[0]初始化为多少没有意义,因为既然n>=1了,无论a初始化为0还是1,for循环都会至少执行一次,而循环中并不涉及到将a代入公式中进行计算,因此a无论初始化为0还是1都不影响最终结果

2.其次是关于变量类型的问题,在上一题中n最大只到30,而本题中最大到45,如果只用int类型变量会溢出,因此改为long long类型

746.使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。

思路:

1.到了本题终于开始有一点发生变化了,首先我们来确定dp数组的下标i和dp[i]的含义。显然i就是第i层楼梯,dp[i]就是到达第i层楼梯需要消耗的费用。但需要注意的一点是楼顶实际上是下标为cost.size()的地方。

2.接下来是对dp数组进行初始化,需要注意根据题目的描述,支付费用后才向上爬楼梯,而一开始我们就已经直接在0或者1的位置,因此到达0和1的楼梯我们是不需要花费体力的,因此dp[0]和dp[1]都应当初始化为0

3.然后是递推公式,要想到达当前楼梯i可以有两种方式,一种是从i - 1楼梯跳一步,一种是从i - 2的楼梯跳两步,因此dp[i]只可能从dp[i - 1] + cost[i - 1]和dp[i - 2] + cost[i - 2]中取得,而题目要求的是最小花费,因此我们取最小值即可。

class Solution {
public:
    int minCostClimbingStairs(vector& cost) {
        vectordp(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()];
    }
};

启发:

1.本题难度不算高,但依然需要注意题目中的各种细节,尤其是楼顶的位置和初始化的想法。

你可能感兴趣的:(代码随想录,算法,leetcode,数据结构,c++,动态规划)