动态规划基础部分03爬楼梯

91 阅读3分钟

今天我们继续来一道动态规划的经典题目

爬楼梯

力扣:(70. 爬楼梯 - 力扣(LeetCode))
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
1 <= n <= 45
示例 1:
输⼊: 2
输出: 2
解释: 有两种⽅法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

示例 2:
输⼊: 3
输出: 3
解释: 有三种⽅法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

如果是第一次接触这种题的朋友可能会感觉⽐较难,但是只要多举⼏个例⼦,就可以发现其规律。
爬到第⼀层楼梯有⼀种⽅法,爬到⼆层楼梯有两种⽅法。那么第⼀层楼梯再跨两步就到第三层 ,第⼆层楼梯再跨⼀步就到第三层。所以到第三层楼梯的状态可以由第⼆层楼梯和到第⼀层楼梯状态推导出来,那么就可以想到动态规划了。

动规五部曲:

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

定义⼀个⼀维数组来记录不同楼层的状态,dp[i]: 爬到第i层楼梯,有dp[i]种⽅法。

2. 确定递推公式

从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数组如何初始化

首先我们要注意题目给出了1 <= n <= 45所以说没有楼梯从0开始的情况,在我们的dp[i]定义:爬到第i层楼梯,有dp[i]种⽅法。所以初始化就应该为:
dp[1] = 1; dp[2] = 2;

4. 确定遍历顺序

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

5. 举例推导dp数组

举例当n为5的时候,dp table(dp数组)应该是这样的

image.png
如果代码出问题了,就把dp table 打印出来,看看究竟是不是和⾃⼰推导的⼀样。 此时⼤家应该发现了,这不就是斐波那契数列么! 唯⼀的区别是,没有讨论dp[0]应该是什么,因为dp[0]在本题没有意义!
以上五部分析完之后,Java代码如下:

class Solution {
    public int climbStairs(int n) {
        int[] dp = new int[n + 1];
        for(int i = 1; i < dp.length; i++){
            if(i < 3)
                dp[i] = i;
            else
                dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];

    }
}

当然依然也可以优化⼀下空间复杂度,和斐波那契数列一样我们只需要维护两个数值就可以了,不需要记录整个序列。这里我就不具体实现了。因为后⾯将讲到的很多动规的题⽬其实都是当前状态依赖前两个,或者前三个状态,都可以做空间上的优化,但我个⼈认为⾯试中能写出版本⼀就够了哈,清晰明了,如果⾯试官要求进⼀步优化空间的话,我们再去优化。因为版本⼀才能体现出动规的思想精髓,递推的状态变化。

注:文章参考微信公共号:代码随想录