LeetCode打家劫舍 动态规划
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 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 。
思路:
打家劫舍是经典的用动态规划解决的问题。我们定义动态规划数组dp,dp[i]就表示以第i间房屋结尾时小偷获得的最大利润(即只偷窃前i间房子,小偷能获得的最大利润)。
注意:这里dp[i],并不代表第i间房一定要偷,只是将前i间房纳入小偷的“考虑范围”之内,具体偷不偷由小偷自己决定。
对于状态转移方程的书写,我们知道当小偷考虑偷窃第i间房也就是nums[i]时,要么就是他之前偷过num[i-1]了,这一家不偷;要么就是他之前只偷过num[i-2],正好可以再偷这一家。一共就这两种情况。
因此状态转移方程为:dp[i] = max(dp[i-2]+nums[i],dp[i-1]),我们只需要看看哪一种情况比较大,就可以得到偷前i间房的最大利润。
代码:
class Solution(object):
def rob(self, nums):
lenth = len(nums)
if lenth==0:#处理特殊情况
return 0
if lenth==1:#如果只有一间房,直接偷完结束
return nums[0]
dp = [0]*lenth#初始化dp
dp[0] = nums[0]#只考虑前一间房,则肯定是偷
dp[1] = max(nums[0],nums[1])#考虑前两间房,因为只能偷一间 比较一下哪个大
#接下来就可以开始用状态转移方程写啦
for i in range(2,lenth):
dp[i] = max(dp[i-2]+nums[i],dp[i-1])
return max(dp)#直接返回dp中的最大值,实际上也就是dp[-1],一样的
小结:
其实我们可以打印最终dp数组看一下,越往后小偷可以累积的金额必然会越来越多。后面我记得打家劫舍题还有环形的变式,也就是把村庄收尾连在一起。看起来因为首尾相连了导致两家不能一起偷了有些复杂。但其实是一个“脑筋急转弯”,我们首先将首尾取出,把剩下的环形村庄变成线形,通过本题的dp方法得到结果,再加上首尾当中较大的那个值即可。具体如果后面写到了再放代码吧,也挺简单的~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了