这是我参与更文挑战的第4天,活动详情查看: 更文挑战
原题
数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值cost[i](下标从 0 开始)。
每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。
请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。
cost的长度范围是[2, 1000]。cost[i]将会是一个整型数据,范围为[0, 999]。
示例?1:
输入:cost = [10, 15, 20]
输出:15
解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。
示例 2:
输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出:6
解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。
重拳出击
这题依然标签还是动态规划,所以还是按照基本套路来。
首先仔细看了看题目,咋一看觉得挺像爬楼梯 但是很重的一个变化是,它每个阶梯有体力消耗,所以我们进行计算时,需要判断每一步 消耗的能量,并且要得出最低消耗情况下的,到达终点的方法。
-
边界条件
根据题目,没有特别明显的边界判断,那就是需要在解题过程中去加了。
-
子问题
根据之前的经验,一开始我们可能会这样设置子问题:以到 n 阶所消耗的体力为dp[n]
但是用草稿演算一下,会发现很难通过 dp[n - 1] 去推断出 dp[n] ,所以我们需要另寻他法。在看一遍题目,可以发现每次能走一个阶梯或者爬两个阶梯,然后我们再从顶部往起始去思考,可以根据最后爬一个阶梯或者爬两个阶梯,然后重新设置子问题,分情况:
dp[n] [0] 为最后一阶走一步, dp[n] [1] 为最后一阶走两步。
然后因为题目规定了,最小有两个台阶,所以我们可以重新设置边界条件
-
方程
根据上一条,可以得知,每走到下个阶梯都要判断两种情况下的走法的消耗,每次取最小那个,然后在加上走一阶或者两阶的消耗,于是得出如下:
当数组
cost[i]遍历结束之后,会得到一个dp[n]数组中存的就是结果 -
结合各种条件写代码
public int minCostClimbingStairs(int[] cost) {
//初始化二维数组
int[][] dp = new int[cost.length + 1][2];
//边界条件
dp[1][0] = 0;
dp[1][1] = 0;
dp[2][0] = cost[1];
dp[2][1] = cost[0];
//根据转移方程变的代码
for (int i = 3; i <= cost.length ; i++) {
dp[i][0] = cost[i - 1] + Math.min(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = cost[i - 2] + Math.min(dp[i - 2][0], dp[i - 2][1]);
}
return Math.min(dp[cost.length][0], dp[cost.length][1]);
}
然后运行,发现 执行耗时击败了99.84%,内存击败了97.12%
结果还不错,这个明显还可以空间优化,每个分支也有大量的重复计算,能够用几个变量去取代多个二维数组。由于懒了,于是就摸鱼🦑了。
收
-
动态规划的子问题设置很重要,一个好的子问题可以极大的减少复杂度