题目 使用最小花费爬楼梯
难度 : 简单
题目大意 :
-
给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
-
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
-
请你计算并返回达到楼梯顶部的最低花费。
这是典型的爬楼梯问题
思路:动态规划 将整个问题转化为若干个子问题
- 我们要到达顶层
n
,思考我们可以怎么到达才花费最小呢?
因为一步可以跨两个或者一个台阶,那么可以从n - 1
或n - 2
级台阶跨,那么此时我们只需求到n - 1
阶台阶的最小花费和n - 2
阶台阶的最小花费即可,这样就转化为了更小的子问题
我们定义一个数组f[n + 1]
, 表示到达第i级台阶要花费的最小代价,那么答案就是f[n]
, 因为起点可以是0
也可以是1
那么初始状态就有了f[0] = f[1] = 0
代码实现 (C++):
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
int f[n + 1];
memset(f, 0x3f, sizeof f);
f[0] = f[1] = 0;
for (int i = 2; i <= n; i ++) {
f[i] = min(f[i - 1] + cost[i - 1], f[i - 2] + cost[i - 2]);
}
return f[n];
}
};
时间复杂度
O
(
n
)
O(n)
O(n)
空间复杂度
O
(
n
)
O(n)
O(n)
我们注意到f[i]
只和f[i - 1]
和f[i - 2]
有关系,那么我们可以用若干变量来表示,将空间复杂度优化为O(1)
, 代码如下 :
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
int f0 = 0, f1 = 0, f;
for (int i = 2; i <= n; i ++) {
f = min(f1 + cost[i - 1], f0 + cost[i - 2]);
f0 = f1, f1 = f;
}
return f;
}
};
空间复杂度
O
(
1
)
O(1)
O(1) 只用到了若干个变量
另外再贴一个记忆化搜索代码实现
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
int f[n + 1];
memset(f, 0x3f, sizeof f);
function<int(int)> dfs = [&](int u) -> int {
if (u <= 1) return 0;
int& res = f[u];
if (res != 0x3f3f3f3f) return f[u];
res = min(dfs(u - 1) + cost[u - 1], dfs(u - 2) + cost[u - 2]);
return res;
};
return dfs(n);
}
};
因为记忆化了,所以时间复杂度,和递推式的是一样的
总结:
- 记忆化搜索和动态规划都是很重要的方法,难度比较大, 要多练
《微语》 : 有时候,最困难的选择才是最正确的选择