2 minutes
Week1015_algorithm
ARTS - Algorithm
补10.15号
746. 使用最小花费爬楼梯
数组的每个索引做为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 costi。
每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。
您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。
示例 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
分析
这道题和70. 爬楼梯 更进一步,是要选出最小花费的那条走法。
我的思考是,首先选择走一步,还是走两步,每次选择时候就找出最小花费的那个,也是就有了如下代码:
public static int minCostClimbingStairs(int[] cost) {
int len = cost.length;
if (len == 2) {
return cost[0] <= cost[1] ? cost[0] : cost[1];
}
return getResult(cost);
}
public static int getResult(int[] cost) {
int res = 0;
int len = cost.length;
int idx = -1;
while (idx + 2 < len) {
int r1 = cost[idx + 1];
int r2 = cost[idx + 2];
if (r1 < r2) {
res += r1;
idx += 1;
} else {
res += r2;
idx += 2;
}
}
return res;
}
这个代码相当于从 -1 位置出发,每次取出走一步或者走两步对应的花费,然后选择花费最小的,然后在此基础上决策下一步,贪心算法的思维。很明显这个解答是错误的,
比如对: 10, 15, 20, 这个算法首先选择是 10, 然后在15,20 中选择了15, 这样总花费是25, 其实我们知道最小花费是15.
为了解决这个问题,考虑到从后往前也算一遍,这样取得两次最小的那个,就是最小花费路径了。比如对 10,15,20, 就会选择15,然后结束。
于是代码就又变成了:
public static int minCostClimbingStairs(int[] cost) {
int len = cost.length;
if (len == 2) {
return cost[0] <= cost[1] ? cost[0] : cost[1];
}
int r1 = getResult1(cost);
int r2 = getResult2(cost);
return r1 <= r2 ? r1 : r2;
}
public static int getResult2(int[] cost) {
int res = 0;
int idx = cost.length;
while (idx - 2 > -1) {
int r1 = cost[idx - 1];
int r2 = cost[idx - 2];
if (r1 < r2) {
res += r1;
idx -= 1;
} else {
res += r2;
idx -= 2;
}
}
return res;
}
public static int getResult1(int[] cost) {
int res = 0;
int len = cost.length;
int idx = -1;
while (idx + 2 < len) {
int r1 = cost[idx + 1];
int r2 = cost[idx + 2];
if (r1 < r2) {
res += r1;
idx += 1;
} else {
res += r2;
idx += 2;
}
}
return res;
}
在提交后,又没有通过,因为存在如下情况:
{0, 2, 3, 2}
从前往后, 会选择 0,2,2 花费 4 从后往前, 会选择 2, 2 花费 4 , 结果返回4。但是我们知道正确路径是 0, 3 , 最小花费是3。
为什么这样的算法会出错?因为它只做到了针对当前路径的最小花费,没有考虑到全部路径的最小花费,所以算法是错的。再看了答案后,学到了正确的算法, 使用递归,直到找到最小花费。
代码
代码如下:
public static int minCostClimbingStairs2(int[] cost) {
int[] m = new int[cost.length + 1];
return dp(cost, m, cost.length);
}
private static int dp(int[] cost, int[] m, int i) {
if (i <= 1) {
return 0;
}
if (m[i] > 0) {
return m[i];
}
return m[i] = Math.min(dp(cost, m, i - 1) + cost[i - 1], dp(cost, m, i - 2) + cost[i - 2]);
}
这个算法每步都会有两个选择,每步都去找到这步选择的最优解,当前的最优解又依赖于下一个的最优解,这样直到走完才能找到整体最优解,于是就得出了答案。