什么?不会动态规划办不了事?
这是一道leetcode上的题目:
https://leetcode-cn.com/problems/house-robber/
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
视频题解在这里~
一、初始化状态
我是一个专业的小偷,遇到这种问题,先从最简单的开始思考
假设房屋数量n = 1时,那么不用说了,最高金额等于这间房屋的现金
假设房屋数量n = 2时,最高金额等于两间房屋中较大金额的那间的现金
假设房屋数量n = 3时,会有几种情况
因为连续偷两间屋子会报警,所以当前这件屋子,要么偷,要么不偷
1)假设我们偷第三间屋子,那么前面只能偷第一间屋子
2)假设我们不偷第三间屋子,那么就相当于偷前两间屋子即n=2的情况
我们取这两种情况的最高金额,也就是
房屋数量n为3的最高金额 = max(前面两间房屋算出的最高金额,第一间屋子金额+第三间屋子金额)
假设房屋数量n= 4时:
1)假设我们偷当前这间屋子,那么就相当于偷前两间屋子的情况加上当前这间屋子的金额
2)假设我们不偷当前这间屋子,那么就相当于偷前三间屋子的情况
我们取两种情况的最大值,即
f(4) = max(f(2)+第4间屋子金额,f(3))
二、递推公式
我是一个聪明的小偷,很快我就发现,事情其实并不简单
今晚能抢到的最高金额 等于:
1) 偷第n间的情况即前n-2间屋子能抢到的最大金额加上第n间屋子金额
2) 不偷第n间的情况即前n - 1间屋子能抢到的最大金额
这两种情况中的较大值
即f(n) = max(f(n-2) + 第n间屋子金额,f(n-1))
我忽然发现,计算每一次的状态,从前面的状态来规划下一步的状态,这不就是我学过的动态规划吗?
三、状态缓存
我是一个爱做笔记的小偷,把每次偷的最高金额记下来,我就不需要重复计算啦
用一个数组dp就可以解决啦,即dp[n] = max(dp[n-2] + 第n间屋子金额,dp[n-1])
这种方法的时间复杂度是O(n),空间复杂度是是O(n)。
python版本代码如下:
class Solution(object):
def rob(self, nums):
if len(nums) == 0:
return 0
if len(nums) <= 2:
return max(nums)
dp = [nums[0],max(nums[0],nums[1])]
for i in range(2,len(nums)):
dp.append(max((dp[i-2] + nums[i]), dp[i-1]))
return max(dp)
四、空间压缩
我是一个精打细算的小偷,很快我又发现,我也不必把每一次计算的金额都记下来, 每次我只需要保留
前面n-2间房屋的算出来的金额 和 前面n-1 间房屋算出来的金额
这两个金额就可以了
这样这种方法的时间复杂度是O(n),空间复杂度是是O(1)。
python版本代码如下:
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) == 0:
return 0
if len(nums) <= 2:
return max(nums)
m1 = nums[0]
m2 = max(nums[0],nums[1])
for i in range(2,len(nums)):
m1,m2 = m2,max(m1+nums[i],m2)
return m2
总结
简易动态规划四部曲
1)初始化状态尝试列出每种状态的情况
2)递推公式由前面的状态尝试找出规律,推出递推公式
3)状态缓存将每一次计算出的状态缓存下来,从而为下一步的状态提供记录,通常是一维数组或者二维数组
4)空间压缩当前状态常常只需要根据前面某些状态进行计算,因此可以不必保留每一次的状态
即一维数组可以变成O(1)的空间复杂度 , 二维数组可以变成一维数组
我真是太聪明啦!
欢迎参加讨论哦 ~
往期精彩:
长按可以关注我哦
你的关注/点赞/在看/分享都是一种支持~
我知道你在看哟