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

题目

一个数组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]);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/290929.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pip install 安装模块包位置及设置Anaconda为默认版本python

01问题 pycharm运行代码找不到模块包pip install不知道安装到哪里了jupyter使用不同版本python 02产生原因 安装了多个版本pythonanaconda本身也带有python 03解决办法 (1)查看当前默认python版本 打开运行窗口Winr&#xff1b; 输入cmd回车&#xff1b; 输入python回车…

初识Web服务器

一、web服务器 1、什么是web服务器&#xff1f; web服务器就是web项目的容器&#xff0c;我们将开发好的web项目部署到web容器中&#xff0c;才能使用网络中的用户通过浏览器进行访问。 一张图带你了解web服务器有啥作用&#xff1a; 在我的电脑上有一个已经做好的项目&#…

linux centos 添加临时ip

### 1.添加ip ip addr add IP/mask dev 网络设备 例&#xff1a;ip addr add 172.104.210.247/24 dev ens5f1 ### 2.启动网卡 ip link set up 网络设备 例&#xff1a;ip link set up ens3f0 ### 3.设置默认路由 ip route add default via GATEWAY 例&#xff1a;ip route add …

python3ide手机安卓版下载,python3下载手机安卓版

大家好&#xff0c;给大家分享一下python3ide手机安卓版下载&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 大家好&#xff0c;给大家分享一下python3ide安卓版官网下载&#xff0c;很多人还不知道这一点。下面详细解释一下python编程代码…

C#编程-描述内存分配

描述内存分配 分配给变量的内存通过两种方式引用&#xff1a;值类型和引用类型。内置数据类型&#xff0c;诸如int、char和float都是值雷兴国。当您声明int变量时&#xff0c;编译器会分配一个内存块以保持该整数值。请思考以下语句&#xff1a; int Num 50;上述语句为保存值…

手机怎么边看视频边记笔记或备忘录?

在这个信息爆炸的时代&#xff0c;我们经常需要通过看培训视频、听网课来不断充实自己。但是&#xff0c;手机屏幕那么小&#xff0c;如何才能在做笔记的同时&#xff0c;又不错过视频的每一个细节呢&#xff1f; 以前&#xff0c;我总是为此头疼。一手拿着手机看视频&#xf…

电脑视频需要分屏怎么做

在当今数字时代&#xff0c;人们对于视频的需求越来越高。有时候&#xff0c;我们可能想在同一屏幕上同时播放多个视频&#xff0c;进行对比、观看、剪辑或者其他目的。那么&#xff0c;视频分屏应该怎么做呢&#xff1f; 在本篇文章中&#xff0c;我们将会详细的为你介绍视频分…

可狱可囚的爬虫系列课程 09:通过 API 接口抓取数据

前面已经讲解过 Requests 结合 BeautifulSoup4 库抓取数据&#xff0c;这种方式在抓取数据时还是比较方便快捷的&#xff0c;但是这并不意味着所有的网站都适合这种方式&#xff0c;并且这也不是抓取数据的最快方式&#xff0c;今天我们来讲一种更快速的获取数据的方式&#xf…

Python selenium模块的安装和配置教程

一、selenium的安装以及简单应用 我们以谷歌浏览器的chromedriver为例 1、在Python虚拟环境中安装selenium模块 pip/pip3 install selenium 2、下载版本符合的webdriver 以chrome谷歌浏览器为例 查看谷歌浏览器的版本 鼠标点击右上角的竖排的三个点&#xff0c;然后选择“…

P1192 台阶问题————C++

目录 台阶问题题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 解题思路Code运行结果 台阶问题 题目描述 有 N N N 级台阶&#xff0c;你一开始在底部&#xff0c;每次可以向上迈 1 ∼ K 1\sim K 1∼K 级台阶&#xff0c;问到达第 N N N 级台阶有多少种不同方…

华为设备命令最全大合集(2024新版),赶紧收藏!

01、华为交换机基础配置命令 01 常用命令视图 02 创建VLAN //用户视图&#xff0c;一般display命令查看信息比较多。 system-view //准备进入系统视图。 [Huawei]vlan 100 //创建vlan 100。 [Huawei-vlan100]quit //退回系统视图。 03 将端口加入到vlan中 [Huawei] inter…

【C语言】字符串 和 ctype.h 中的函数 练习

前面总结了有关字符串和ctype.h的文章&#xff0c;接下来就以几个例子来练习一下&#xff0c;以巩固之前的基础概念。注意&#xff1a;以下示例都有更简单更高效的解决方法&#xff0c;但本次仅以巩固基础为目的&#xff0c;所以方法可能稍作繁琐 Leetcode 344.反转字符串 编…

Spring Boot 整合多 Redis 数据源配置及操作

Spring Boot 整合多 Redis 数据源配置及操作 简介 本文档介绍了如何在Spring Boot应用程序中配置和操作多个Redis数据源。通过配置多个RedisConnectionFactory和RedisTemplate&#xff0c;可以实现对多个Redis数据源的整合&#xff0c;以便在应用程序中灵活地使用不同的Redis…

windows2012 安装mysql5.7

windows2012 安装mysql5.7 1.安装1.解压文件夹2.把my文件拷入没有sql安装目录3.编辑my文件4.按照下方进行配置5.cmd进入bin目录6.出现丢失文件7.安装这个文件即可解决8.开始进行安装&#xff0c;输入mysqld install9.初始化mysql&#xff08;mysqld --initialize --console&…

python识别验证码+灰度图片base64转换图片

一、为后面识别验证码准备 1、图片base64转换为 上文中的base64,后面的就是包含Base64编码的PNG图像的字符串复制下来 import base64 from PIL import Image import io# 这里是你的Base64编码的字符串 base64_data "iVBORw0KGgoAAAANSUhEUgAAAG8AAAAkCAIAAAAIOPOYAAAJ1E…

提供电商Api接口-100种接口,淘宝,1688,抖音商品详情数据安全,稳定,支持高并发

Java是一种高级编程语言&#xff0c;由Sun Microsystems公司于1995年推出&#xff0c;现在属于Oracle公司开发和维护。Java以平台无关性、面向对象、安全性、可移植性和高性能著称&#xff0c;广泛用于桌面应用程序、嵌入式系统、企业级服务、Android移动应用程序等。 接口是Ja…

软件测试方法分类-按测试对象划分

接上一篇,下来我们再细讲,第四个维度的分类, 软件测试方法分类-按测试对象划分 本章节重点介绍非功能测试的相关知识,因为功能测试的基本在之前的分类都是有涉及的。 一、非功能测试 1,性能测试(Performance Testing) 检查系统是否满足需求规格说明书中规定的性能。 …

Clion STM32 开发环境配置教程

Clion STM32 开发环境配置教程 STM32 CubeMX&#xff08;6.5&#xff09; 下载固件库 若固件库还未下载&#xff0c;可在启动界面点击&#xff0c;INSTALL/REMOVE下载所需要的固件库 选中对应固件库&#xff0c;点击Install即可 Clion&#xff08;2023.3.1&#xff09; 略 …

从零实现一套低代码(保姆级教程) --- 【14】实现头像组件和徽标容器

前话 文章开始前&#xff0c;先解决一下之前的某个错误。 在InputComponent中&#xff0c;如果是弹窗类型的组件&#xff0c;我们点击按钮会把ModalComponent组件弹出来。同时&#xff0c;我们要把key传进去。 return (<div>{getComponent()}// 把valueKey穿过去<Mo…

Java集合框架和泛型

1.Java集合框架 架构图&#xff1a; Java的集合框架是一组用于存储和操作数据的类和接口。它提供了各种数据结构&#xff0c;如列表、集合、映射等&#xff0c;以及用于操作这些数据结构的算法和工具。Java集合框架位于Java.util包中&#xff0c;并且是Java编程中常用的核心组…