打家劫舍问题

article/2025/9/12 17:55:29

打家劫舍问题

  • 最近碰见这种问题实在是太多了,感觉还是有必要学习一下打家劫舍以及其变种问题
  • 这一类问题采用的都是动态规划的解法

一些练习题目

6378. 最小化旅行的价格总和

198. 打家劫舍I

213. 打家劫舍 II

337. 打家劫舍 III

2560. 打家劫舍 IV

1 、打家劫舍I

题目描述

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

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

数据范围

1 < = n u m s . l e n g t h < = 100 1 <= nums.length <= 100 1<=nums.length<=100
0 < = n u m s [ i ] < = 400 0 <= nums[i] <= 400 0<=nums[i]<=400

思路

动态规划三部曲:

  • 状态表示:
    • 集合: f [ i ] f[i] f[i] 表示偷到第 i i i 间的金钱数量
    • 属性: 最大值
  • 状态计算:列出状态转移方程
    • f [ i ] = m a x ( f [ i − 2 ] + n u m s [ i ] , f [ i − 1 ] ) f[i] = max(f[i-2] + nums[i], f[i-1]) f[i]=max(f[i2]+nums[i],f[i1])
      偷第 i i i 间房, 可以从集合 f [ i − 2 ] , f [ i − 1 ] f[i-2],f[i-1] f[i2],f[i1] 转移过来, 如果偷, f [ i ] = f [ i − 2 ] + n u m s [ i ] f[i] = f[i-2] + nums[i] f[i]=f[i2]+nums[i]
      如果不偷, f [ i ] = f [ i − 1 ] f[i] = f[i-1] f[i]=f[i1] 。对于这两种情况可以取一个最大值。

代码

  • 没有经过空间优化

def rob(self, nums: List[int]) -> int:n = len(nums)f = [0] * (n+2)for i in range(n):f[i+2] = max(f[i+1], f[i] + nums[i])return f[-1]
  • 空间优化

def rob(self, nums: List[int]) -> int:n = len(nums)f0, f1 = 0, 0 for i in range(n):f0 = max(f1, f0 + nums[i])f0, f1 = f1, f0return f1

2、打家劫舍II

题目描述

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

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

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2), 然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

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

数据范围

1 < = n u m s . l e n g t h < = 100 1 <= nums.length <= 100 1<=nums.length<=100
0 < = n u m s [ i ] < = 400 0 <= nums[i] <= 400 0<=nums[i]<=400

思路

动态规划三部曲:

  • 状态表示:
    • 集合: f [ i ] f[i] f[i] 表示偷到第 i i i 间的金钱数量
    • 属性: 最大值
  • 状态计算:列出状态转移方程
    • 和打家劫舍I不同的是,这次房屋是环绕形的,其实还是分割子问题
      • 因为关键在于环形:可以考虑将环拆开,然后分类讨论,如果偷 n u m s [ 0 ] nums[0] nums[0] ,那么必定不偷 n u m s [ 1 ] , n u m s [ n − 1 ] nums[1],nums[n-1] nums[1],nums[n1] ,那么就是取 n u m s [ 0 ] + r o b ( n u m s [ 2 : n − 1 ] ) nums[0] + rob(nums[2:n-1]) nums[0]+rob(nums[2:n1])
      • 如果不偷 n u m s [ 0 ] nums[0] nums[0], 那么就可以偷 n u m s [ 1 ] , n u m s [ n − 1 ] nums[1],nums[n-1] nums[1],nums[n1] ,那么就转换成了 r o b ( n u m s [ 1 : ] ) rob(nums[1:]) rob(nums[1:])
        的问题。

代码


def rob(self, nums: List[int]) -> int:def rob1(nums: List[int]) -> int:n = len(nums)f0, f1 = 0, 0 for i in range(n):f0 = max(f1, f0 + nums[i])f0, f1 = f1, f0return f1return max(nums[0] + rob1(nums[2:-1]), rob1(nums[1:]))

3、打家劫舍III

题目描述

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 r o o t root root

除了 r o o t root root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 r o o t root root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。

在这里插入图片描述

示例 1:

输入:root = [3,2,3,null,3,null,1]
输出:7
解释:小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7

在这里插入图片描述

示例 2:

输入:root = [3,4,5,1,3,null,1]
输出:9
解释:小偷一晚能够盗取的最高金额 4 + 5 = 9

数据范围

  • 树的节点数在 [ 1 , 104 ] [1, 104] [1,104] 范围内
  • 0 < = N o d e . v a l < = 1 0 4 0 <= Node.val <= 10^4 0<=Node.val<=104

思路

动态规划三部曲:

  • 状态表示:
    • 集合: 采用 f ( o ) f(o) f(o) 表示选择当前节点所能偷取的最大钱数, g ( o ) g(o) g(o) 表示不选择当前节点所能偷取的最大钱数。
    • 属性: 最大值
  • 状态计算:列出状态转移方程
    • 和打家劫舍I,II不同的是,这次针对的是树,其实相当于是在树上做 D P DP DP
      • 考虑 f ( o ) , g ( o ) f(o),g(o) f(o),g(o),怎么转移过来即可
        • f ( o ) = g ( l ) + g ( r ) + o . v a l f(o) = g(l) + g(r) + o.val f(o)=g(l)+g(r)+o.val
        • g ( o ) = m a x ( f ( l ) , g ( l ) ) + m a x ( f ( r ) , g ( r ) ) g(o) = max(f(l), g(l)) + max(f(r), g(r)) g(o)=max(f(l),g(l))+max(f(r),g(r))

代码


def rob(self, root: Optional[TreeNode]) -> int:def dfs(root):if not root:return 0, 0fl, gl = dfs(root.left)fr, gr = dfs(root.right)f = root.val + gl + grg = max(fl, gl) + max(gr, fr)return f, greturn max(dfs(root))

4、打家劫舍IV

题目描述

沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。

由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋 。

小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额 。

给你一个整数数组 n u m s nums nums 表示每间房屋存放的现金金额。形式上, 从左起第 i i i 间房屋中放有 n u m s [ i ] nums[i] nums[i] 美元。

另给你一个整数 k k k ,表示窃贼将会窃取的 最少 房屋数。小偷总能窃取至少 k k k 间房屋。

返回小偷的 最小 窃取能力。

示例 1:

输入:nums = [2,3,5,9], k = 2
输出:5
解释:小偷窃取至少 2 间房屋,共有 3 种方式:

  • 窃取下标 0 和 2 处的房屋,窃取能力为 max(nums[0], nums[2]) = 5 。
  • 窃取下标 0 和 3 处的房屋,窃取能力为 max(nums[0], nums[3]) = 9 。
  • 窃取下标 1 和 3 处的房屋,窃取能力为 max(nums[1], nums[3]) = 9 。
    因此,返回 min(5, 9, 9) = 5 。

示例 2:

输入:nums = [2,7,9,3,1], k = 2
输出:2
解释:共有 7 种窃取方式。窃取能力最小的情况所对应的方式是窃取下标 0 和 4 处的房屋。返回 max(nums[0], nums[4]) = 2 。

数据范围

  • 1 < = n u m s . l e n g t h < = 105 1 <= nums.length <= 105 1<=nums.length<=105
  • 1 < = n u m s [ i ] < = 109 1 <= nums[i] <= 109 1<=nums[i]<=109
  • 1 < = k < = ( n u m s . l e n g t h + 1 ) / 2 1 <= k <= (nums.length + 1)/2 1<=k<=(nums.length+1)/2

思路

  • 这个是套了一层二分答案壳子的打家劫舍I
    • 首先最小化最大窃取能力可以直接联想到二分答案
    • 如果小偷的窃取能力越强,越有可能偷够k间房屋,所以答案具有单调性

代码


def minCapability(self, nums: List[int], k: int) -> int:n = len(nums)def check(mid):f0, f1 = 0, 0for x in nums:f0 = max(f1, f0 + (mid >= x))f1, f0 = f0, f1return f1 >= kl, r = min(nums), max(nums)while l < r:mid = (l + r) // 2if check(mid):r = midelse:l = mid + 1return l

喜欢的话,请多多为我点赞吧~

在这里插入图片描述


http://chatgpt.dhexx.cn/article/308IiehB.shtml

相关文章

经典动态规划:打家劫舍系列问题

打家劫舍系列总共有三道&#xff0c;难度设计非常合理&#xff0c;层层递进。第一道是比较标准的动态规划问题&#xff0c;而第二道融入了环形数组的条件&#xff0c;第三道更绝&#xff0c;让盗贼在二叉树上打劫. House Robber | public int rob(int[] nums);题目很容易理解…

【算法】动态规划(三)——打家劫舍系列问题

目录 一、前言 二、打家劫舍 &#xff08;1&#xff09;198. 打家劫舍Ⅰ • 整体代码&#xff1a; &#xff08;2&#xff09;213. 打家劫舍 II • 题目分析 • 整体代码&#xff1a; &#xff08;3&#xff09;337. 打家劫舍Ⅲ • 思路分析 • 整体代码&#xff1a; 三、补充知…

动态规划之打家劫舍系列

前言 打家劫舍问题是一种非常经典的有限制条件的动态规划问题&#xff0c;按理说&#xff0c;不是一种特殊的类型&#xff0c;但是因为力扣上纯纯的出了三道题&#xff08;1&#xff0c;2&#xff0c;3&#xff09;来考察&#xff0c;题目的难度是依次递进的&#xff0c;还结合…

动态规划之打家劫舍

动态规划之打家劫舍 文章目录 动态规划之打家劫舍1. "198. 打家劫舍"2. "198. 打家劫舍&#xff08;变种&#xff1a;输出路径&#xff09;"3. "213. 打家劫舍 II"4. "337. 打家劫舍 III" 1. “198. 打家劫舍” dp数组定义&#xff1a…

oracle 根据部分字段去重

问题&#xff1a;在oracle中使用group by分组&#xff0c;group by子句中必须包含所有的select中的字段和order by子句中的字段。 在不使用group by子句的情况下&#xff0c;进行分组。&#xff08;根据部分字段分组&#xff09; over()分析函数 原sql SELECTIM. ID mediaGrou…

oracle字段去重查询,oracle怎么去重查询

oracle去重查询的方法是&#xff1a; oracle 数据库多字段去重 方法介绍&#xff1a;distinct 关键字、group by 、row_number ()over(partition by 列 order by 列 desc) 我的需求是&#xff1a;根据某几列去重 查询出去重后的全部信息。最后我选择的是第三种方法。 我的想法&…

oracle 数据去重方法

1. 创建表&#xff1a; -- Create table create table TEST_USER (user_id NUMBER(3),user_name VARCHAR2(20),user_age NUMBER(3) ) tablespace GUAN_TABLESPACEpctfree 10initrans 1maxtrans 255storage(initial 64Knext 1Mminextents 1maxextents unlimited);--测试数据…

oracle 字符串去重

select regexp_replace(1,1,3,5,5, ([^,])(,\1)*(,|$), \1\3) from dual;注意&#xff1a; 但是&#xff0c;这个去重&#xff0c;必须建立在排序的基础上&#xff0c;如果listagg拼接出来的数值像 a, b, a, c 这时候&#xff0c;该正则就会失效。

MYSQL/ORACLE多字段去重-根据某字段去重

通过百度上的答案多数无效 自己搞了个 使用oracle row_number()函数&#xff0c;给每个同名的加一个序号&#xff0c;最后筛选第n个想同的即可 oracle与mysql不同 1.oracel 多字段distinct(字段名去重) group by去重失效 可以用row_number() over(partition) 给同名列加个序号…

Oracle 数据去重

在Oracle数据库中删除重复数据 一&#xff0c;查询及删除重复记录的SQL语句 Person01表&#xff1a; 1. 查询表中多余的重复数据&#xff0c;根据ID字段来判断是否重复 SELECT * FROM PERSON01 WHERE ID IN (SELECT ID FROM PERSON01 GROUP BY ID HAVING COUNT(ID) > 1)…

Oracle根据多列去重

&#xff08;1&#xff09;distinct 关键词 distinct用于返回唯一不同的值&#xff0c;可作用于单列和多列 但必须将其放在开头&#xff0c;否则会提示错误 而若在其后添加多个变量名&#xff0c;则返回的将是这多个变量名不同时重复的列&#xff0c;因而使用distinct筛选某…

oracle 数据库去重查询

oracle数据库中有如下一张表&#xff0c;包含id,loginid,name,researchtime等字段&#xff0c;其中name字段中的数据有重复&#xff0c;查询数据时要重复数据只取一条&#xff0c;利用row_number ()over(partition by 列 order by 列 desc)方法实现 1:select a.,row_number() o…

oracle去重函数

1、distinct &#xff08;1&#xff09;、常用的distinct select distinct column from table; &#xff08;2&#xff09;、统计去重后数量 select count(distinct column) from table;–查去重后数量 &#xff08;3&#xff09;、distinct必须放在开头 select id, distinct n…

oracle 数据库 去重查询

oracle 数据库多字段去重 方法介绍&#xff1a;distinct 关键字、group by 、row_number ()over(partition by 列 order by 列 desc) 我的需求是&#xff1a;根据某几列去重 查询出去重后的全部信息。最后我选择的是第三种方法。 我的想法&#xff1a;我想找出一种更简单的方…

Oracle实现去重的两种方式总结

业务场景 需要查询某数据&#xff0c;由于需要三张表关联查询&#xff0c;查询结果如下&#xff1a; 原始SQL语句 SELECT D.ORDER_NUM AS "申请单号" ,D.CREATE_TIME ,D.EMP_NAME AS "申请人",(SELECT extractvalue(t1.row_data,/root/row/FI13_wasteNam…

mysql默认密码的查找与修改

注&#xff1a;此方法仅可用于初始安装数据库或学习时使用&#xff0c;在实际生产中会使所有数据库文件删除&#xff0c;故应先提前备份相关重要数据&#xff0c;以免造成不必要的损失&#xff0c;请谨慎使用。 若使用mysqld –initialize初始化mysql数据库&#xff0c;会产生一…

rpm安装mysql后密码_CentOs安装Mysql和配置初始密码

装载自&#xff1a;https://www.cnblogs.com/FlyingPuPu/p/7783735.html 一、Mysql下载安装 使用上传命令上传至/home目录&#xff0c;如&#xff1a;rz命令(yum install -y lrzsz) 添加mysql仓库(-Uvh后面接的为你下载的rpm文件名) sudo rpm -Uvh mysql57-community-release-e…

MySQL初始密码的查看

问题&#xff1a;在安装MySQL过程中&#xff0c;以管理员身份运行cmd后进入MySQL的bin目录&#xff0c;然后输入命令“mysqld --initialize”后没有显示初始密码&#xff0c;没办法进行后续的登录怎么办&#xff1f; 1.打开你的MySQL的安装目录下的data文件夹&#xff08;就是…

如何找到mysql的初始密码_如何查看mysql的初始密码

如何查看mysql的初始密码 发布时间:2020-08-26 11:50:11 来源:亿速云 阅读:95 作者:Leah 今天就跟大家聊聊有关如何查看mysql的初始密码,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。 查看mysql的初始密码的…

[mysql]linux服务器mysql默认密码查看

通过 cat /var/log/mysqld.log | grep password 命令查看数据库的密码