面试算法88:爬楼梯的最少成本

news/2024/11/23 2:39:10/

题目

一个数组cost的所有数字都是正数,它的第i个数字表示在一个楼梯的第i级台阶往上爬的成本,在支付了成本cost[i]之后可以从第i级台阶往上爬1级或2级。假设台阶至少有2级,既可以从第0级台阶出发,也可以从第1级台阶出发,请计算爬上该楼梯的最少成本。例如,输入数组[1,100,1,1,100,1],则爬上该楼梯的最少成本是4,分别经过下标为0、2、3、5的这4级台阶,如图14.1所示(注意:最上一级台阶是没有成本的)。
在这里插入图片描述

分析

分析1

爬上一个有多级台阶的楼梯自然需要若干步。按照题目的要求,每次爬的时候既可以往上爬1级台阶,也可以爬2级台阶,也就是每一步都有两个选择。这看起来像是与回溯法有关的问题。但这个问题不是要找出有多少种方法可以爬上楼梯,而是计算爬上楼梯的最少成本,即计算问题的最优解,因此解决这个问题更适合运用动态规划。

分析2:确定状态转移方程

这个问题要求计算爬上楼梯的最少成本,可以用函数f(i)表示从楼梯的第i级台阶再往上爬的最少成本。如果一个楼梯有n级台阶(台阶从0开始计数,从第0级一直到第n-1级),由于一次可以爬1级或2级台阶,因此最终可以从第n-2级台阶或第n-1级台阶爬到楼梯的顶部,即f(n-1)和f(n-2)的最小值就是这个问题的最优解。
应用动态规划的第1步是找出状态转移方程,即用一个等式表示其中某一步的最优解和前面若干步的最优解的关系。根据题目的要求,可以一次爬1级或2级台阶,既可以从第i-1级台阶爬上第i级台阶,也可以从第i-2级台阶爬上第i级台阶,因此,从第i级台阶往上爬的最少成本应该是从第i-1级台阶往上爬的最少成本和从第i-2级台阶往上爬的最少成本的较小值再加上爬第i级台阶的成本。这个关系可以用状态转移方程表示为f(i)=min(f(i-1),f(i-2))+cost[i]。

分析3

上述状态转移方程有一个隐含的条件,即i大于或等于2。如果i小于2怎么办?如果i等于0,则可以直接从第0级台阶往上爬,f(0)等于cost[0]。如果i等于1,题目中提到可以从第1级台阶出发往上爬,因此f(1)等于cost[1]。

public class Test {public static void main(String[] args) {int[] cost = {1, 100, 1, 1, 100, 1};int result = minCostClimbingStairs(cost);System.out.println(result);}public static int minCostClimbingStairs(int[] cost) {int len = cost.length;return Math.min(helper(cost, len - 2), helper(cost, len - 1));}private static int helper(int[] cost, int i) {if (i < 2) {return cost[i];}return Math.min(helper(cost, i - 2), helper(cost, i - 1)) + cost[i];}
}

分析4

上述代码看起来很简捷,但时间效率非常糟糕。时间效率是面试官非常关心的问题,如果应聘者的解法的时间效率糟糕则很难通过面试。根据前面的递归代码,为了求得f(i)需要先求得f(i-1)和f(i-2)。如果将求解过程用一个树形结构表示(如图14.2中求解f(9)的过程),就能发现在求解过程中有很多重复的节点。例如,求解f(9)需要求解f(8)和f(7),而求解f(8)和f(7)都需要求解f(6),这就意味着在求解f(9)的过程中有重复计算。
求解f(i)这个问题的解,依赖于求解f(i-1)和f(i-2)这两个子问题的解,由于求解f(i-1)和f(i-2)这两个子问题有重叠的部分,如果只是简单地将状态转移方程转换成递归的代码就会带来严重的效率问题,因为重复计算是呈指数级增长的。
为了避免重复计算带来的问题,一个常用的解决办法是将已经求解过的问题的结果保存下来。在每次求解一个问题之前,应先检查该问题的求解结果是否已经存在。如果问题的求解结果已经存在,则不再重复计算,只需要从缓存中读取之前求解的结果。

public class Test {public static void main(String[] args) {int[] cost = {1, 100, 1, 1, 100, 1};int result = minCostClimbingStairs(cost);System.out.println(result);}public static int minCostClimbingStairs(int[] cost) {int len = cost.length;int[] dp = new int[len];helper(cost, len - 1, dp);return Math.min(dp[len - 2], dp[len - 1]);}private static void helper(int[] cost, int i, int[] dp) {if (i < 2) {dp[i] = cost[i];}else if (dp[i] == 0) {helper(cost, i - 2, dp);helper(cost, i - 1, dp);dp[i] = Math.min(dp[i - 2], dp[i - 1]) + cost[i];}}
}

分析5:空间复杂度为O(n)的迭代代码

也可以自下而上地解决这个过程,也就是从子问题入手,根据两个子问题f(i-1)和f(i-2)的解求出f(i)的结果。

public class Test {public static void main(String[] args) {int[] cost = {1, 100, 1, 1, 100, 1};int result = minCostClimbingStairs(cost);System.out.println(result);}public static int minCostClimbingStairs(int[] cost) {int len = cost.length;int[] dp = new int[len];dp[0] = cost[0];dp[1] = cost[1];for (int i = 2; i < len; i++) {dp[i] = Math.min(dp[i - 2], dp[i - 1]) + cost[i];}return Math.min(dp[len - 2], dp[len - 1]);}
}

分析6:空间复杂度为O(1)的迭代代码

上述迭代代码还能做进一步的优化。前面用一个长度为n的数组将所有f(i)的结果都保存下来。求解f(i)时只需要f(i-1)和f(i-2)的结果,从f(0)到f(i-3)的结果其实对求解f(i)并没有任何作用。也就是说,在求每个f(i)的时候,需要保存之前的f(i-1)和f(i-2)的结果,因此只要一个长度为2的数组即可。

public class Test {public static void main(String[] args) {int[] cost = {1, 100, 1, 1, 100, 1};int result = minCostClimbingStairs(cost);System.out.println(result);}public static int minCostClimbingStairs(int[] cost) {int[] dp = new int[] {cost[0], cost[1]};for (int i = 2; i < cost.length; i++) {dp[i % 2] = Math.min(dp[0], dp[1]) + cost[i];}return Math.min(dp[0], dp[1]);}
}

http://www.ppmy.cn/news/1295273.html

相关文章

el-table表格动态添加列。多组数据拼接和多层级数据的处理

提示&#xff1a;el-table表格动态添加列 文章目录 前言一、多组数据拼接二、多层级处理三、实际应用中&#xff0c;为避免闪屏&#xff0c;可以表格数据统一渲染总结 前言 需求&#xff1a;富文本编辑器 一、多组数据拼接 <template><div class"test">…

Spark---RDD介绍

文章目录 1.Spark核心编程2.RDD介绍2.1.RDD基本原理2.2 RDD特点1.弹性2.分布式 &#xff1a;数据存储在大数据集群的不同节点上3.数据集 &#xff1a;RDD封装了计算逻辑&#xff0c;并不保存数据4.数据抽象 &#xff1a;RDD是一个抽象类&#xff0c;具体实现由子类来实现5. 不可…

推荐弱光图像增强算法比较《Lightening Network for Low-Light Image Enhancement》(附带DLN可执行程序)

文章链接&#xff1a;https://ieeexplore.ieee.org/document/9141197 文章代码&#xff1a;https://github.com/WangLiwen1994/DLN 很经典的一个工作&#xff0c;其实并没有特别好讲的&#xff0c;因为并不是广为流传的工作 唯一值得说的就是比较好更改网络结构以及用于我们自…

Docker Zookeeper 安装 简单教程

现在各种组件大部分都能找到Docker的镜像了&#xff0c;Docker容器化安装很多复杂中间件都变得非常轻松了。 1.拉取镜像 以下命令默认是拉取最新版本 zookeeper:latest docker pull zookeeper 注: 若要拉取指定版本如3.7&#xff0c;则可以执行命令 docker pull zookeeper:…

HTB Bizness

Bizness 2024年1月7日 10:16:41User Nmap Not shown: 997 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0) 80/tcp open http nginx 1.18.0 |_http-server-header: nginx/1.18.0 |_http-title:

十一、工具盒类(MyQQ)(Qt5 GUI系列)

目录 ​编辑 一、设计需求 二、实现代码 三、代码解析 四、总结 一、设计需求 抽屉效果是软件界面设计中的一种常用形式&#xff0c;可以以一种动态直观的方式在有限大小的界面上扩展出更多的功能。本例要求实现类似 QQ 抽屉效果。 二、实现代码 #include "dialog.…

STM32 学习(二)GPIO

目录 一、GPIO 简介 1.1 GPIO 基本结构 1.2 GPIO 位结构 1.3 GPIO 工作模式 二、GPIO 输出 三、GPIO 输入 1.1 传感器模块 1.2 开关 一、GPIO 简介 GPIO&#xff08;General Purpose Input Output&#xff09;即通用输入输出口。 1.1 GPIO 基本结构 如下图&#xff0…

vue3防抖函数封装与使用,以指令的形式使用

utils/debounce.js /*** 防抖函数* param {*} fn 函数* param {*} delay 暂停时间* returns */ export function debounce(fn, delay 500) {let timer nullreturn function (...args) {// console.log(arguments);// const args Array.from(arguments)if (timer) {clearTim…