【动归】B008_使用最小花费爬楼梯(递归 | 记忆化搜索 | dp)

一、题目描述

On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed).

Once you pay the cost, you can either climb one or two steps. You need to find minimum cost to reach the top of the floor, 
and you can either start from the step with index 0, or the step with index 1.

Input: cost = [10, 15, 20]
Output: 15
Explanation: Cheapest is start on cost[1], pay that cost and go to the top.

二、题解

方法一:

题意:

  • 初始位置可以选择平台或者第一格楼梯。
  • 楼层顶部在数组末尾紧挨的后面。
  • 每次跳 1 两格或者 2 格,求花费最少的 cost 跳到楼顶。

递归三部曲:

  • 结束条件:如果跳到了楼梯的最后一格或者外部,即 i>=n,可以比较最少这种策略带来的花费和minCost的大小,取最小值。
  • 本层递归的责任:花费跳出本格楼梯的体力,并且在本层楼梯选择两个策略进行尝试。
static int[] COST;
int minCost = Integer.MAX_VALUE;
public int minCostClimbingStairs(int[] cost) {
  COST = cost;
  dfs(0, cost.length , 0);
  dfs(1, cost.length , 0);	
  return minCost;
}
private void dfs(int i, int n, int curCost) {
  if (i >= n) {
    if (curCost < minCost) {
      minCost = curCost;
    }
    return;
  }
  curCost += COST[i];
  dfs(i+1, n, curCost);
  dfs(i+2, n, curCost);
}

复杂度分析

  • 时间复杂度: O ( 2 n ) O(2^n)
  • 空间复杂度: O ( n ) O(n)

方法二:记忆化递归

使用 memo 数组来记录某些已有计算,减少递归分支的产生。唯一要注意的是:因为我们可以从第 0/1 格楼梯开始,所以 memo 数组需要分别保存两种状态的计算结果。

public int minCostClimbingStairs(int[] cost) {

  COST = cost;
  memo = new int[cost.length];

  Arrays.fill(memo, -1);
  int choice1 = dfsWithMemo(0, cost.length);
  Arrays.fill(memo, -1);
  int choice2 = dfsWithMemo(1, cost.length);

  return Math.min(choice1, choice2);
}
private int dfsWithMemo(int i, int n) {
  if (i >= n) {	
    return 0;	//跳出楼梯,返回即可
  }
  if (memo[i] == -1) {
    memo[i] = COST[i] + Math.min(dfsWithMemo(i+1, n), dfsWithMemo(i+2, n));
  }
  return memo[i];
}

复杂度分析

  • 时间复杂度: O ( n ) O(n)
  • 空间复杂度: O ( n ) O(n)

方法三:dp

因为从某一格楼梯开始,子问题可能是前 1 格或前 2 格花费的最小者,存在最优子结构的求解,所以可用 dp 做。

  • 初始值
    • dp[0] = cost[0],表示从第 0 格开始跳的体力花费。
    • dp[1] = cost[1],表示从第 1 格开始跳的体力花费。
  • 状态定义dp[i] 的值代表直到走到第 i 格楼梯最小体力花费。
  • 转移方程: dp[i] = cost[i] + min(dp[i-1], dp[i-2])
public int minCostClimbingStairs(int[] cost) {
  final int N = cost.length;
  int[] dp = new int[N];
  dp[0] = cost[0];
  dp[1] = cost[1];
  for (int i = 2; i < N; i++) {
    dp[i] = COST[i] + Math.min(dp[i-1], dp[i-2]);
  }
  return Math.min(dp[N-1], dp[N-2]);//跳到楼顶有两种选择(从倒数第二层或倒数第一层开始两格或一格)
}

复杂度分析

  • 时间复杂度: O ( n ) O(n)
  • 空间复杂度: O ( n ) O(n)
发布了461 篇原创文章 · 获赞 102 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/104614235