题干
首先放入最后运行正确的答案
class Solution {
public int minCostClimbingStairs(int[] cost) {
//到达楼顶的最低花费 = Math.min(到达次一层的最低花费+次一层的花费值,到达次二层的最低花费)
int[] dp = new int[cost.length];
dp[0]=cost[0];
dp[1]=cost[1];
for(int i=2;i<cost.length;i++)
{
dp[i] = Math.min(dp[i-1],dp[i-2])+cost[i];
}
return Math.min(dp[dp.length-1],dp[dp.length-2]);
}
}
从注释可以看出运用了动态规划的思维.从当前所站的位置去考虑最低花费,是一个逆推的过程。
动态规划方程为到达楼顶的最低花费 = Math.min(到达次一层的最低花费+次一层的花费值,到达次二层的最低花费)。
边界值为下标0或者下标1。
下面是一些错误解答
第一种
class Solution {
public int minCostClimbingStairs(int[] cost) {
int[] comsumption = new int[2];
comsumption[0] = cost[0];
comsumption[1] = cost[1];
int index0 = 0;
int index1 = 1;
while(index0 < cost.length-2)
{
comsumption[0] += cost[index0+1]>=cost[index0+2]?cost[index0+2]:cost[index0+1];
if(cost[index0+1]>=cost[index0+2])
{
index0 = index0+2;
}
else
{
index0 = index0+1;
}
}
while(index1 < cost.length-2)
{
comsumption[1] += cost[index1+1]>=cost[index1+2]?cost[index1+2]:cost[index1+1];
if(cost[index1+1]>=cost[index1+2])
{
index1 = index1+2;
}
else
{
index1 = index1+1;
}
}
return Math.min(comsumption[0],comsumption[1]);
}
}
因为只涉及到了当前楼层,次级楼层和次次级楼层,所以设想使用滚动数组comsumption来存储次级楼层和次次级楼层的最低花费。因为题干中明确指出可以选择下标初始值为0或者1,因此我设计将其分为两种不同情况分开进行。下面以下标为0作为例子来描述我的设计思路。
while(index0 < cost.length-2)
{
comsumption[0] += cost[index0+1]>=cost[index0+2]?cost[index0+2]:cost[index0+1];
if(cost[index0+1]>=cost[index0+2])
{
index0 = index0+2;
}
else
{
index0 = index0+1;
}
}
这部分代码可以看出与运行成功的代码的最大不同就是指针的含义不同。上述代码中指针是站在已经花费体力的台阶上计算下一步要如何选择才能达到最低花费(1),而运行成功代码的指针则是站在还未花费体力的台阶上回顾来路去思考如何能花费最低(2)。这点似乎就是一开始无法解决这道题的最为致命的地方。因为想方法(1)这样去设计每一步选择只能是当前最优解,而方法(2)这种做法才能实现达到目标的最优解。
使用方法(1)无法通过的情况如下:
最低花费应当是2,但是按照方法(1)得出的最优解为3,流程为0->1->2。
在看了一些评论之后我修改了我自己的想法,写下新的错误解答。
第二种
class Solution {
public int minCostClimbingStairs(int[] cost) {
int index = cost.length;
int value = 0;
while(index >= 2)
{
value += cost[index-1]<cost[index-2]?cost[index-1]:cost[index-2];
if(cost[index-1]<cost[index-2])
{
index = index-1;
}
else
{
index = index-2;
}
}
return value;
}
}
上述代码则是使用了逆推的形式,假设我现在处在楼顶回顾来路去计算最低花费,但这仍旧是一个错误答案。
使用第二种错误做法来解答上图的测试用例,流程如下:
顶层->1>下标为1的2
正确做法,流程如下:
dp[2]=2->dp[3]=3->顶层dp=2
下面来分析两个方法的不同:
正确做法的下标是规律递增,最低花费确实是动态变化的;第二种错误做法则是下标和最低花费同步运动。这是因为得出最后结果需要对来路每一步都进行判断,从而选出最低花费,而错误做法仍是在做对当前最优解的累加,只不过是换了个方向。
通过对上述的分析 动态规划需要遍历每一步判断从而得出结果 同时最终结果需要和之前的结果有关联。
更新与滚动数组相结合的解法 空间复杂度为常数值
class Solution {
public int minCostClimbingStairs(int[] cost) {
//到达楼顶的最低花费 = Math.min(到达次一层的最低花费+次一层的花费值,到达次二层的最低花费)
//int[] dp = new int[cost.length];
int[] dp = new int[2];
dp[0]=cost[0];
dp[1]=cost[1];
for(int i=2;i<cost.length;i++)
{
//dp[i] = Math.min(dp[i-1],dp[i-2])+cost[i];
dp[i%2] = Math.min(dp[0],dp[1])+cost[i];
}
//return Math.min(dp[dp.length-1],dp[dp.length-2]);
return Math.min(dp[0],dp[1]);
}
}