算法刷题打卡第40天:打家劫舍

news2024/11/15 22:33:17

打家劫舍

难度:中等

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

解法一、递归

思路:
根据对题目分析可以发现,此问题可以拆分为多个子问题来解决,由于不是主要推荐解法,不过多叙述。

时间复杂度: O ( 2 n ) O(2^n) O(2n),其中 n n n 是数组的长度。
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组的长度。空间复杂度取决于递归使用的栈空间。

class Solution:
    def rob(self, nums: List[int]) -> int:
        def maxMoney(index):
            if index == -1:
                return 0
            if index == 0:
                return nums[0]
            return max(maxMoney(index-1), maxMoney(index-2) + nums[index])
        return maxMoney(len(nums)-1)

解法二、去重递归

思路:
此解法是对递归的一个优化,由于递归中存在过多重复计算因素,所以可以采用备忘录的方式进行优化。

时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组的长度,第一次递归某个值需要调用递归函数。
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组的长度,备忘录所占空间。

class Solution:
    def rob(self, nums: List[int]) -> int:
        savelis = [None] * len(nums)
        def maxMoney(index):
            if index == -1:
                return 0
            if index == 0:
                return nums[0]
            if savelis[index-1] is None:
                savelis[index-1] = maxMoney(index-1)
            if savelis[index-2] is None:
                savelis[index-2] = maxMoney(index-2)
            return max(savelis[index-1], savelis[index-2] + nums[index])
        return maxMoney(len(nums)-1)

解法三、动态规划(重点)

思路:
如果你对于动态规划还不是很了解,或者没怎么做过动态规划的题目的话,那么 House Robber (小偷问题)这道题是一个非常好的入门题目。本文会以 House Robber 题目为例子,讲解动态规划题目的四个基本步骤。

动态规划的的四个解题步骤是:

  • 定义子问题
  • 写出子问题的递推关系
  • 确定 DP 数组的计算顺序
  • 空间优化(可选)

下面我们一步一步地进行讲解。

步骤一:定义子问题
稍微接触过一点动态规划的朋友都知道动态规划有一个“子问题”的定义。什么是子问题?子问题是和原问题相似,但规模较小的问题。例如这道小偷问题,原问题是“从全部房子中能偷到的最大金额”,将问题的规模缩小,子问题就是“从 k k k 个房子中能偷到的最大金额”,用 f ( k ) f(k) f(k) 表示。

在这里插入图片描述
可以看到,子问题是参数化的,我们定义的子问题中有参数 k k k。假设一共有 n n n 个房子的话,就一共有 n n n 个子问题。动态规划实际上就是通过求这一堆子问题的解,来求出原问题的解。这要求子问题需要具备两个性质:

  • 原问题要能由子问题表示。例如这道小偷问题中, k = n k=n k=n 时实际上就是原问题。否则,解了半天子问题还是解不出原问题,那子问题岂不是白解了。
  • 一个子问题的解要能通过其他子问题的解求出。例如这道小偷问题中, f ( k ) f(k) f(k) 可以由 f ( k − 1 ) f(k−1) f(k1) f ( k − 2 ) f(k−2) f(k2) 求出,具体原理后面会解释。这个性质就是教科书中所说的“最优子结构”。如果定义不出这样的子问题,那么这道题实际上没法用动态规划解。

小偷问题由于比较简单,定义子问题实际上是很直观的。一些比较难的动态规划题目可能需要一些定义子问题的技巧。

步骤二:写出子问题的递推关系

这一步是求解动态规划问题最关键的一步。然而,这一步也是最无法在代码中体现出来的一步。在做题的时候,最好把这一步的思路用注释的形式写下来。做动态规划题目不要求快,而要确保无误。否则,写代码五分钟,找 bug 半小时,岂不美哉?

我们来分析一下这道小偷问题的递推关系:

假设一共有 n n n 个房子,每个房子的金额分别是 H 0 , H 1 , … , H n − 1 H_0, H_1, \dots, H_{n-1} H0,H1,,Hn1,子问题 f ( k ) f(k) f(k) 表示从前 k k k 个房子(即 H 0 , H 1 , … , H k − 1 H_0, H_1, \dots, H_{k-1} H0,H1,,Hk1 中能偷到的最大金额。那么,偷 k k k 个房子有两种偷法:

在这里插入图片描述
k k k 个房子中最后一个房子是 H k − 1 H_{k-1} Hk1 。如果不偷这个房子,那么问题就变成在前 k − 1 k−1 k1 个房子中偷到最大的金额,也就是子问题 f ( k − 1 ) f(k−1) f(k1)。如果偷这个房子,那么前一个房子 H k − 2 H_{k-2} Hk2 显然不能偷,其他房子不受影响。那么问题就变成在前 k − 2 k−2 k2 个房子中偷到的最大的金额加上 H k − 1 H_{k-1} Hk1 的金额。两种情况中,选择金额较大的一种结果。
f ( k ) = m a x { f ( k − 1 ) , H k − 1 + f ( k − 2 ) } f(k)=max\{f(k−1),H_{k−1} +f(k−2)\} f(k)=max{f(k1),Hk1+f(k2)}

在写递推关系的时候,要注意写上 k = 0 k=0 k=0 k = 1 k=1 k=1 的基本情况:

  • k = 0 k=0 k=0 时,没有房子,所以 f ( 0 ) = 0 f(0)=0 f(0)=0
  • k = 1 k=1 k=1 时,只有一个房子,偷这个房子即可,所以 f ( 1 ) = H 0 f(1) = H_0 f(1)=H0

这样才能构成完整的递推关系,后面写代码也不容易在边界条件上出错。

步骤三:确定 DP 数组的计算顺序

在确定了子问题的递推关系之后,下一步就是依次计算出这些子问题了。在很多教程中都会写,动态规划有两种计算顺序,一种是自顶向下的、使用备忘录的递归方法,一种是自底向上的、使用 dp 数组的循环方法。不过在普通的动态规划题目中,99% 的情况我们都不需要用到备忘录方法,所以我们最好坚持用自底向上的 dp 数组。

DP 数组也可以叫”子问题数组”,因为 DP 数组中的每一个元素都对应一个子问题。如下图所示,dp[k] 对应子问题 f ( k ) f(k) f(k),即偷前 k k k 间房子的最大金额。

在这里插入图片描述
那么,只要搞清楚了子问题的计算顺序,就可以确定 DP 数组的计算顺序。对于小偷问题,我们分析子问题的依赖关系,发现每个 f ( k ) f(k) f(k) 依赖 f ( k − 1 ) f(k−1) f(k1) f ( k − 2 ) f(k−2) f(k2)。也就是说,dp[k] 依赖 dp[k-1]dp[k-2],如下图所示。
在这里插入图片描述
那么,既然 DP 数组中的依赖关系都是向右指的,DP 数组的计算顺序就是从左向右。这样我们可以保证,计算一个子问题的时候,它所依赖的那些子问题已经计算出来了。

确定了 DP 数组的计算顺序之后,我们就可以写出题解代码。

时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组的长度。
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组的长度,DP 数组所占空间。

class Solution:
    def rob(self, nums: List[int]) -> int:
    
	    # 子问题:
	    # f(k) = 偷 [0..k) 房间中的最大金额
	
	    # f(0) = 0
	    # f(1) = nums[0]
	    # f(k) = max{ rob(k-1), nums[k-1] + rob(k-2) }
	    
        length = len(nums)
        dp = [0, nums[0]] + [None] * (length - 1)
        for i in range(2, length+1):
            dp[i] = max(dp[i-1], dp[i-2]+nums[i-1])
        return dp[length]

解法四、动态规划空间优化(重点)

思路:
空间优化是动态规划问题的进阶内容了。对于初学者来说,可以不掌握这部分内容。

空间优化的基本原理是,很多时候我们并不需要始终持有全部的 DP 数组。对于小偷问题,我们发现,最后一步计算 f ( n ) f(n) f(n) 的时候,实际上只用到了 f ( n − 1 ) f(n−1) f(n1) f ( n − 2 ) f(n−2) f(n2) 的结果。 n − 3 n−3 n3 之前的子问题,实际上早就已经用不到了。那么,我们可以只用两个变量保存两个子问题的结果,就可以依次计算出所有的子问题。下面的动图比较了空间优化前和优化后的对比关系:

请添加图片描述
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组的长度。
空间复杂度: O ( 1 ) O(1) O(1)

class Solution:
    def rob(self, nums: List[int]) -> int:
        p1, p2 = 0, 0
        
        # 每次循环,计算“偷到当前房子为止的最大金额”
        for i in nums:
	        # 循环开始时,p2 表示 dp[k-1],p1 表示 dp[k-2]
	        # dp[k] = max{ dp[k-1], dp[k-2] + i }
            p1, p2 = p2, max(p2, p1 + i)
            # 循环结束时,p2 表示 dp[k],p1 表示 dp[k-1]
            
        return p2

来源:力扣(LeetCode)
题目来源链接:https://leetcode.cn/problems/house-robber
动态规划思路链接:https://leetcode.cn/problems/house-robber/solutions/138131/dong-tai-gui-hua-jie-ti-si-bu-zou-xiang-jie-cjavap/

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

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

相关文章

全解一款六面体结构化网格划分利器-NUMECA IGG

作者 |卢工FunRun 仿真秀优秀讲师 导读:前不久,VIP群有人提问:“老师,NUMECA如何计算带蜗壳叶轮机呢”?笔者使用NUMECA FINE/Turbo(以下简称Turbo)软件解决叶轮机械气动性能仿真计算已有三年多&…

nRF Connect的使用

一、工具简介 nRF Connect是NORDIC开发的一款低功耗蓝牙测试APP,仅支持安卓。可以扫描和探索低功耗蓝牙设备并与它们通信。 蓝牙通信的核心是向硬件发送数据和接收硬件传回来的数据。 二、准备项 Android手机 蓝牙硬件 三、使用简介 1、进入界面 (1&…

某科技公司防火墙配置与管理

目录 杭州继保南瑞电子科技有限公司… 1 公司简介…2需求分析… 错误!未定义书签。公司网络拓扑图…4IP 地址规划 …4设备选型…5技术介绍…6 6.1 DMZ …6 6.2 VPN …6 6.3 NAT …6 6.4 ACL …7项目实施…7 7.1 DMZ 区域配置及结果测试 …7 7.1.1 防火墙基本配置…8 7.1.2 内网…

粉笔通过上市聆讯:上半年营收14.5亿 腾讯经纬高瓴是股东

雷递网 雷建平 12月7日职业教育平台粉笔科技今日通过聆讯,准备在港交所上市。花旗、中金(香港)和美银证券为其联席保荐人。粉笔科技此次募资用途为丰富课程内容、扩大学员群体、加强内容及技术开发能力等。上半年营收14.51亿粉笔科技成立于20…

# spring-security(一)

一、权限管理简介 1、什么是权限管理 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。 ​ 权限管理…

学习笔记-3-SVM-9-Twin SVM

Outline 1. Motivation 2. Geometry 3. Algebraic manipulation 4. Observations ------------------------------ 1. Motivation Twin SVM的基本出发点是做二分类时,为什么我们只用1个分割超平面,为什么不能用两个呢? 这里是想用两个…

CVT变速器中壳体吊机设计

目 录 1 绪论 1 1.1 课题的研究目的和意义 1 1.2 课题研究的内容 1 2 CVT变速器中壳体吊机总体设计 3 2.1 性能参数 3 2.2 确定主要工作机构和金属结构的形式 4 2.2.1 确定主要工作机构形式 4 2.2.2 金属结构选型 11 2.3 载荷的计算 13 2.3.1 自重载荷 13 2.3.2 起升载荷 14 2.…

HTTP常见状态码

网上都有状态码的说明但是有些不全所以我特此在这里整理一下,这个图来自小林大佬的图 1xx 100 表示客户还需要继续发送请求 101 客户要求服务器根据请求转换HTTP协议版本号 2xx 200 成功 201 提示知道新文件的URL 202 接受和处理、但处理未完成 203 返…

Redis缓存优化、本地锁及分布式锁的入门使用思想实现

Redis缓存优化、本地锁及分布式锁的入门使用思想实现 1、依赖启动器引入 <!-- redis --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency><!…

行为管理(锐捷业务软件篇)

大家好&#xff0c;我是小杜&#xff0c;古人云“好好学习&#xff0c;天天向上”。我要学习“古人”好榜样──三更鸡鸣、五......做为新时代的五好青年只能说“小杜”做不到啊&#xff01;不过提早到公司学习还是可以的。 之前了解了软件产品如何部署实施后。我们今天来看看对…

大一html5期末大作业 :基于html实现非遗文化网页设计题材【传统文化木雕】7个页面

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

如何压缩png图片大小?

png这种图片格式大家应该都熟悉吧&#xff1f;&#xff0c;跟jpg格式有着同样高的使用率。只不过png图片偏于专业化&#xff0c;而jpg更具平常性。举个不恰当的例子Jpg格式就好比相当于口语&#xff0c;而png格式相当于比较正式的书面用语的意思&#xff0c;所以很多小伙伴经常…

C++11标准模板(STL)- 算法(std::make_heap)

定义于头文件 <algorithm> 算法库提供大量用途的函数&#xff08;例如查找、排序、计数、操作&#xff09;&#xff0c;它们在元素范围上操作。注意范围定义为 [first, last) &#xff0c;其中 last 指代要查询或修改的最后元素的后一个元素。 数据结构的堆物理结构是数…

走向大模型、大算力、大数据:特斯拉与毫末的自动驾驶AI路径寻踪

2022行至年终&#xff0c;各种年度总结也纷至沓来。要说最近的大事件&#xff0c;一定少不了&#xff1a;自动驾驶又双叒寒冬了。大量“报忧不报喜”的新闻&#xff0c;说明2022年自动驾驶行业确实出现了一定程度的波动&#xff1a;激光雷达鼻祖德国ibeo和独角兽Argo.ai相继破产…

less基础

less基础 1、维护CSS的弊端 CSS是一门非程序语言&#xff0c;没有变量、函数、SCOPE(作用域) 等概念 CSS需要书写大量看似没有逻辑的代码&#xff0c;CSS冗余度是比较高的不方便维护及扩展&#xff0c;不利于复用CSS没有很好的计算能力非前端开发工程师来讲&#xff0c;往往会因…

[附源码]Python计算机毕业设计Django作业查重系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

AirPods Pro 2用户反馈出现耗电严重情况,Find My功能是其最大亮点

多位 AirPods Pro 2 用户反馈&#xff0c;在升级安装最新版本之后出现了耗电严重的情况。AirPods Pro 2 充电盒的电池即使在不使用的情况下也会出现耗电情况。受影响的用户反馈在不使用状态下&#xff0c;一晚上可以消耗 10% 的电量。 这些受影响的 AirPods Pro 2 用户反馈&…

业界认可+1!网易云信入选首批智慧教育产品和服务供应商名录

近日&#xff0c;2022&#xff08;第二十一届&#xff09;中国互联网大会在深圳成功召开。大会期间&#xff0c;由中国互联网协会智慧教育工作委员会、中国信息通信研究院合办的智慧教育论坛如期举行&#xff0c;论坛以“数智启新聚势&#xff0c;教育点亮未来”为主题&#xf…

Android AOSP和Android-X86源码下载编译终极普法

Android AOSP和Android-X86源码下载编译终极普法 引言 最近有朋友在询问怎么下载Android AOSP源码和Android-X86源码&#xff0c;编译学习&#xff01;其实这个说简单也简单&#xff0c;说复杂吗也不复杂。但是难在真的干起来&#xff01;凯子哥的风格吗&#xff0c;既然朋友们…

JavaSe

软件&#xff1a; 一系列按照特定顺序组织的计算机数据和指令的集合、有系统软件&#xff08;window、linux&#xff09;和应用软件&#xff08;QQ、微信&#xff09;之分。 人机交互方式&#xff1a; 图形化界面&#xff08;GUI&#xff09;鼠标直接点击&#xff08;简单直观…