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

news2025/5/26 11:28:47

题目

一个数组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.coloradmin.cn/o/1355632.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-搭建集成Mybatis的项目

本文介绍了如何在IntelliJ IDEA中使用SpringBoot和Mybatis构建Java Web应用程序。通过本文的学习&#xff0c;读者将了解如何使用IntelliJ IDEA快速搭建一个基于SpringBoot和Mybatis的Java Web应用程序&#xff0c;提高开发效率。IntelliJ IDEA是一款功能强大的Java集成开发环境…

训练过程中验证精度高于训练精度可能的原因

可能的原因&#xff1a; 1. 在数据集很大的时候&#xff0c;loss下降很明显。train loss是平均一个epoch内的所有loss&#xff0c;比如第一个epoch的loss是2.3,2.2,2.1...0.7,0.6 平均train loss是1.5&#xff0c; 而val的时候是用已经训练了一个epoch的model进行测试的&…

【NP】规约与问题复杂度

目录 多项式时间规约复杂度类 多项式时间规约 Polynomial-Time Reductions &#xff1a;如果问题 Y Y Y 的任意实例可以通过多项式次数的标准计算步骤&#xff0c;加上对解决问题 X X X 的黑盒的多项式次数调用来解决&#xff0c;那么称问题 Y Y Y 可以在多项式时间归约为问…

网络爬虫之金融数据前后端实现

基金监控项目实战 一 设计思想 爬虫 采集基金公司的数据 单线程爬虫&#xff0c;用requests进行处理 web开发 前端 负责页面的设计的设计 HTMLCSSJavaScriptecharts 后端 负责调用爬虫获取数据&#xff0c;传输给前端 Flask框架 流程思维导图&#xff1a; 二 前端设计 …

【AI】使用LoFTR进行图像匹配测试Demo

LoFTR图像匹配的源码解析我们在上篇文章中已经写了&#xff0c;对于怎么试用一下&#xff0c;我这边再啰嗦一下。 0.环境搭建 详细的搭建教程请点击链接查看&#xff0c;这里只对需要特殊注意的地方做阐述 1.创建的Python环境采用python3.8的环境&#xff0c;因为文章发布较早…

CMake入门教程【核心篇】添加库(add_library)

&#x1f608;「CSDN主页」&#xff1a;传送门 &#x1f608;「Bilibil首页」&#xff1a;传送门 &#x1f608;「本文的内容」&#xff1a;CMake入门教程 &#x1f608;「动动你的小手」&#xff1a;点赞&#x1f44d;收藏⭐️评论&#x1f4dd; 文章目录 1. 基本用法2.STATIC…

DevOps(3)

目录 11.描述root账户&#xff1f; 12.如何在发出命令时打开命令提示符&#xff1f; 14.Linux系统下交换分区的典型大小是多少&#xff1f; 15.什么是符号链接&#xff1f; 11.描述root账户&#xff1f; root账户就像一个系统管理员账户&#xff0c;允许你完全控制系统。 …

[DevOps-03] Build阶段-Maven安装配置

一、简要说明 下载安装JDK8下载安装Maven二、复制准备一台虚拟机 1、VM虚拟复制克隆一台机器 2、启动刚克隆的虚拟机,修改IP地址 刚刚克隆的虚拟机 ,IP地址和原虚拟的IP地址是一样的,需要修改克隆后的虚拟机IP地址,以免IP地址冲突。 # 编辑修改IP地址 $ vi /etc/sysconfig…

6. Mybatis 缓存

6. Mybatis 缓存 MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率MyBatis系统中默认定义了两级缓存 一级缓存二级缓存 默认情况下&#xff0c;只有一级缓存&#xff08;SqlSession级别的缓存&#xff0c;也称为本地缓存&…

OpenGL如何基于glfw库 进行 点线面 已解决

GLFW是现在较流行、使用广泛的OpenGL的界面库&#xff0c;而glut库已经比较老了。GLEW是和管理OpenGL函数指针有关的库&#xff0c;因为OpenGL只是一个标准/规范&#xff0c;具体的实现是由驱动开发商针对特定显卡实现的。由于OpenGL驱动版本众多&#xff0c;它大多数函数的位置…

国内CRM系统哪个品牌比较好?

国内CRM系统哪个品牌比较好? 作为简道云第一个以“独立套件”开发出来的业务系统&#xff0c;对于简道云CRM我从来都是“昂首挺胸”的骄傲和自豪的&#xff01; 下面就从功能、价格、自定义3大块儿来介绍下简道云CRM管理系统&#xff01; CRM管理系统&#xff0c;即开即用​…

一加 Buds 3正式发布:普及旗舰音质 一加用户首选

1月4日&#xff0c;一加新品发布会正式推出旗下新款耳机一加 Buds 3。延续一加经典美学&#xff0c;秉承音质完美主义追求&#xff0c;一加 Buds 3全面普及一加旗舰耳机体验&#xff0c;其搭载旗舰同款“超清晰同轴双单元”&#xff0c;配备49dB 4000Hz超宽频主动降噪&#xff…

进程和计划任务

一、程序进程 1.1 什么是程序 是一组计算机能识别和执行的指令&#xff0c;运行于电子计算机上&#xff0c;满足人们某种需求的信息化工具 用于描述进程要完成的功能&#xff0c;是控制进程执行的指令集 二、进程 2.1 什么是进程&#xff1f; 记载到内存中运行&#xff…

Visual Saliency Transformer (VST) 源代码实现

1.论文信息 1.1论文标题&#xff1a;Visual Saliency Transformer (VST) Nian Liu, Ni Zhang, Kaiyuan Wan, Junwei Han, and Ling Shao 1.2 Github源代码地址&#xff1a;https://github.com/nnizhang/VST 1.3论文下载链接&#xff1a;http://openaccess.thecvf.com/conte…

vue+elementUI+XLSX.utils.sheet_to_json实现复杂表头的导入功能

导入表excel 问题 XLSX.utils.sheet_to_json方法不带参数的调用&#xff0c;无法解析我们的复杂表头的excel表格&#xff0c;因此&#xff0c;我们需要通过传参数&#xff0c;来指定表头的真实起始位置。 关键代码分析&#xff1a;XLSX.utils.sheet_to_json XLSX.utils.shee…

Linux驱动开发笔记(六):用户层与内核层进行数据传递的原理和Demo

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/135384355 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

【管理篇 / 恢复】❀ 07. macOS下用命令刷新固件 ❀ FortiGate 防火墙

【简介】随着苹果电脑的普及&#xff0c;很多管理员都会通过苹果电脑对飞塔防火墙进行管理。当防火墙需要命令状态下刷新固件时&#xff0c;在macOS下用命令刷新固件&#xff0c;将会是一个小小的挑战。 首先是硬件的连接&#xff0c;USB配置线的USB一头&#xff0c;接入MAC的U…

【springboot+mybatis实现CURD模版项目-Jesus】

springbootmybatis实现CURD模版项目-Jesus STEP 1 项目创建 1.1 新建Spring Initializr项目   1.2 选择需要的依赖 springboot有2.7.2直接选272STEP 2 配置更改 2.1更改maven配置   2.2 检查项目配置jdk、sdk、jre版本一致   2.3 检查pom文件&#xff0c;Maven-Reload pr…

线程池的shutdown和shutdownnow的区别

1、先说结论 shutdown ---- 不再接收新的任务&#xff0c;但是已经在执行中和队列中的任务会等待执行完成&#xff0c; 对workers中空闲的线程执行interrupt shutdownnow ---- 不再接收新的任务&#xff0c;清空队列的任务&#xff0c;对works中所有的线程执行interrupt&…

Vue3 自定义Hooks大全:一站式解决你的疑惑!

前言 不知道喜欢 vue3 的小伙伴和我是不是一样&#xff0c;刚上手vue3 的时候 对自定义hooks 一脸懵逼&#xff0c;在一些视频网站学习的时候老师讲解到自定义hooks 最喜欢用 加减乘除来描述 自定义hooks 是咋用的&#xff0c;可能是我理解能力比较差吧&#xff0c;我看了这个…