ps:最近在学习动态规划,昨天的零钱兑换理解的有点费劲,所以今天找了一题更为基础的动规题学习一下。
这一题实质就是斐波那契数列。
学习链接:https://labuladong.gitbook.io/algo/dong-tai-gui-hua-xi-lie/dong-tai-gui-hua-ji-ben-ji-qiao/dong-tai-gui-hua-xiang-jie-jin-jie
题目链接:https://leetcode-cn.com/problems/climbing-stairs
题目描述:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
2 阶 + 1 阶
解题思路:
1.确定base case。
当爬第0级台阶或爬第1级台阶,只有1种方案。即dp[0] = 1, dp[1] = 1。
2.确定【状态】,原问题和子问题种会变化的变量。
爬第几级台阶会不断的向base case靠近,唯一的【状态】是台阶级数。
3.确定【选择】,导致【状态】产生变化的行为。
一次爬1级台阶还是2级台阶就是【选择】,从而导致台阶数发生变化。
4.明确dp函数/数组的定义。
函数的参数就是状态转移中变化的量,即【状态】。
函数的返回值就是要求计算的量,即多少种方法。
所以定义dp[i]: 爬到第i层楼梯,有dp[i]种方法
5.确定状态转移方程:
用 f(x) 表示爬到第 x 级台阶的方案数,考虑最后一步可能跨了一级台阶,也可能跨了两级台阶,所以状态转移方程:f(x)=f(x−1)+f(x−2)。
代码:
方法一:时间复杂度O(n),空间复杂度O(n)。
class Solution {
public:
int climbStairs(int n) {
vector<int> dp(n+1, 0);
int i;
dp[0] = 1;
dp[1] = 1;
for(i = 2;i <=n; i++)
{
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
};
方法二(优化):
f(x)只和f(x−1) 与f(x−2) 有关,只需要保存前两个值,可以用「滚动数组思想」把空间复杂度优化成O(1)。
class Solution {
public:
int climbStairs(int n) {
vector<int> dp(3, 0);
int i;
dp[0] = 1;
dp[1] = 1;
for(i = 2; i <= n; i++)
{
dp[2] = dp[0] + dp[1];
dp[0] = dp[1];
dp[1] = dp[2];
}
return dp[1];
}
};