前言
动态规划章节第4篇。
记录 七十三【746. 使用最小花费爬楼梯】
一、题目阅读
给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
示例 1:
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。
示例 2:
输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。
提示:
2 <= cost.length <= 1000
0 <= cost[i] <= 999
二、尝试实现
分析题目,确定方法
- 给了一个cost数组,cost[i]表示从第i个台阶向上需要花费cost[i],i=0,cost[0]也有含义。向上一步可以走1个或者走2个台阶。问到楼顶最小花费是多少?
- 到楼顶的状态可以从倒数第一个台阶或者倒数第二个台阶的状态推出来。在倒数第一个台阶上,往上迈一步,一步走1个台阶到楼顶;在倒数第二个台阶上,往上迈一步,一步走2个台阶到楼顶;
- 由状态转移的现象,所以应该用 动态规划。
思路
- 确定dp数组含义和下标含义:
- 先定义下楼顶,当cost.size =2(提示说至少为2),楼顶是2;当cost.size =3,楼顶是3;所以,楼顶int up = cost.size();
- dp[i]代表当楼顶是i时,花费最少是dp[i]。
- 确定状态转移公式=递推公式。
- 想要到达楼顶,一步走2个台阶:dp[i-2]+cost[i-2]。到i-2用的最小花费加上该楼梯自己的cost,往上迈一步;
- 想要到达楼顶,一步走1个台阶:dp[i-1]+cost[i-1]。到i-1用的最小花费加上该楼梯自己的cost,往上迈一步;
- 比较取最小值。
- 所以递推公式:dp[i] = min (dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
- 初始化:
- dp[0]没有含义;
- dp[1]没有含义,但是可以初始化为0,dp[1] =0;
- dp[2]=min(cost[0],cost[1]);
- dp[3] = min(dp[2] +cost[2],cost[1])。发现:cost[1] = dp[1] + cost[1],所以才把dp[1] =0。
- 后面使用递推公式。
- 遍历顺序:从前往后。
代码实现
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
//楼顶 = cost.size()
int up = cost.size();
//定义dp数组。含义:dp[i]代表楼顶是i时,需要的最低花费是多少。
vector<int> dp(up+1,0);//首先都初始化为0.
//初始化
dp[1] = 0;//没含义。因为cost长度至少2,所以楼顶至少是2.
dp[2] = min(cost[0],cost[1]);//楼顶是2,初始化。
//开始遍历。从前往后
for(int i =3;i < dp.size();i++){
//递推公式
dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp[up];
}
};
三、参考学习
学习内容
- 思路步骤和二、中一致。第一种实现就是二、中代码实现。
- 优化一下,因为一个状态只跟前两个状态相关,所以数组大小可以只设定为2个:
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
//楼顶 = cost.size()
int up = cost.size();
//定义dp数组。含义:dp[0]是dp[i-2]的值,dp[1]是dp[i-1]的值
int dp[2];
//初始化
dp[0] = 0;
dp[1] = 0;
//开始遍历。从前往后
for(int i =2;i <= up;i++){
//递推公式
int mincost = min(dp[1]+cost[i-1],dp[0]+cost[i-2]);
dp[0] =dp[1];
dp[1] = mincost;
}
return dp[1];
}
};
- 扩展:第一步是花费的,最后一步不花费。
- 题意重新分析:此时cost[i]的含义是:站在下标i的台阶上,需要花费cost[i]。
- dp数组含义:站到下标为i的台阶上,最少花费是dp[i]。
- 状态转移公式:先比较min(dp[i-1],dp[i-2]),再站到下标为i的台阶,花费cost[i]。
- 初始化:dp[0] = cost[0] ;dp[1] = cost[1]。
- 代码实现:
// 版本一
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
vector<int> dp(cost.size());
dp[0] = cost[0]; // 第一步有花费
dp[1] = cost[1];
for (int i = 2; i < cost.size(); i++) {
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i];
}
// 注意最后一步可以理解为不用花费,所以取倒数第一步,第二步的最少值
return min(dp[cost.size() - 1], dp[cost.size() - 2]);
}
};
总结
分析出使用动态规划,再使用动规五部曲即可。
有点感觉分析一道题目用什么方法才是最重要的。毕竟自己做题并没有人来帮忙分类。
(欢迎指正,转载标明出处)