198打家劫舍 动态规划与DFS剪枝

打家劫舍

在这里插入图片描述

动态规划解法,二维dp

目的是为了求最大金额,状态应该是与最大金额有关的
状态转移:
由于每个元素都会有两种状态,而且这两种状态和前一个元素的状态有关,因此dp数组是二维的,每个元素也需要记录两种状态

dp[i][0]: 表示对第i个元素,不偷的最大金额
dp[i][1]: 表示对第i个元素,偷的最大金额
# 状态转移 
dp[i][0] = max( dp[i-1][0], dp[i-1][1] ) # 无法确定那个大,取最大的
dp[i][1] = dp[i-1][0] + nums[i]

# 初始状态
dp[0][0] = 0
dp[0][1] = nums[0]

完整代码如下:

class Solution:
    def rob(self, nums: List[int]) -> int:
        length = len(nums)
        if length == 0:
            return 0
        dp = [[0]*2 for _ in range(length)]
        dp[0][0] = 0
        dp[0][1] = nums[0]
        for i in range(1, length):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1])
            dp[i][1] = dp[i-1][0] + nums[i]
        return max(dp[length-1][0], dp[length-1][1])

利用滚动数组,减少空间的消耗

class Solution:
    def rob(self, nums: List[int]) -> int:
        length = len(nums)
        if length == 0:
            return 0
        dp = [[0]*2 for _ in range(2)]
        dp[0][0] = 0
        dp[0][1] = nums[0]
        for i in range(1, length):
            dp[i%2][0] = max(dp[(i-1) % 2][0], dp[(i-1) % 2][1])
            dp[i%2][1] = dp[(i-1) % 2][0] + nums[i]
        return max(dp[(length-1) % 2][0], dp[(length-1) % 2][1])

发现上面有点多余,可以再简单一点

class Solution:
    def rob(self, nums: List[int]) -> int:
        length = len(nums)
        if length == 0:
            return 0
        dp = [[0]*2]
        dp[0][0] = 0
        dp[0][1] = nums[0]
        for i in range(1, length):
            tmep = dp[0][0]
            dp[0][0] = max(dp[0][0], dp[0][1])
            dp[0][1] = tmep + nums[i]
        return max(dp[0][0], dp[0][1])

再简单一点

class Solution:
    def rob(self, nums: List[int]) -> int:
        length = len(nums)
        if length == 0:
            return 0
        dp = [0]*2
        dp[0] = 0
        dp[1] = nums[0]
        for i in range(1, length):
            tmep = dp[0]
            dp[0] = max(dp[0], dp[1])
            dp[1] = tmep + nums[i]
        return max(dp[0], dp[1])

还能优化一下

class Solution:
    def rob(self, nums: List[int]) -> int:
        length = len(nums)
        if length == 0:
            return 0
        dp = [0]*2
        dp[0] = 0
        dp[1] = nums[0]
        for i in range(1, length):
            dp[0] , dp[1] = max(dp[0], dp[1]), dp[0]+ nums[i]
        return max(dp[0], dp[1])

更简单的解法, 一维dp

dp[i] 表示前i个空间,并且第i个空间选取时,的最大值。

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0

        size = len(nums)
        if size == 1:
            return nums[0]
        
        dp = [0] * size
        dp[0] = nums[0]
        dp[1] = max(nums[0], nums[1])
        for i in range(2, size):
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
        
        return dp[size - 1]

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/house-robber/solution/da-jia-jie-she-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上述方法使用了数组存储结果。考虑到每间房屋的最高总金额只和该房屋的前两间房屋的最高总金额相关,因此可以使用滚动数组,在每个时刻只需要存储前两间房屋的最高总金额。

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0

        size = len(nums)
        if size == 1:
            return nums[0]
        
        first, second = nums[0], max(nums[0], nums[1])
        for i in range(2, size):
            first, second = second, max(first + nums[i], second)
        
        return second

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/house-robber/solution/da-jia-jie-she-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

滚动数组还是取模方式比较方便

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0

        size = len(nums)
        if size == 1:
            return nums[0]
        dp = [0]*2
        dp[0], dp[1] = nums[0], max(nums[0], nums[1])
        for i in range(2, size):
            dp[i%2] = max(dp[(i-1) % 2], dp[(i-2) % 2] + nums[i] )
        
        return dp[(size-1) % 2]
class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0

        size = len(nums)
        if size == 1:
            return nums[0]
        dp = [0]*3
        dp[0], dp[1] = nums[0], max(nums[0], nums[1])
        for i in range(2, size):
            dp[i%3] = max(dp[(i-1) % 3], dp[(i-2) % 3] + nums[i] )
        return dp[(size-1) % 3]

DFS解法

DFS 与 迭代的区别

本质上和动态规划的解法是一样的,都是状态的转移,不同的是DFS的方式是从后往前推的递归形式,而动态规划的方式是从前往后推的迭代形式,动态规划更简洁一点。而DFS如果用树状图来描述,则思路比较清楚,就是代码写起来有点复杂。
另外在递归的过程中有大量的重复,因此需要用字典结构来进行剪枝,空间复杂度O(n),而迭代形式的滚动数组是O(1),迭代的一大好处就是可以使用滚动数组,减少空间的使用。
在这里插入图片描述

class Solution:
    def rob(self, nums: List[int]) -> int:
        length = len(nums)
        if length == 0:
            return 0
        dict1 = {}
        def _dfs(n,flg):
            if n == 0 and flg == 0:
                return 0
            if n == 0 and flg == 1:
                return nums[0]
            if flg == 0:
                v0 = 0; v1 = 0
                if (n-1, 0) in dict1.keys():
                    v0 = dict1[(n-1, 0)]
                else: v0 = _dfs(n-1, 0); dict1[(n-1, 0)] = v0
                if (n-1, 1) in dict1.keys():
                    v1 = dict1[(n-1, 1)]
                else: v1 = _dfs(n-1, 1); dict1[(n-1, 1)] = v1
                v = max(v0,v1)
                dict1[(n,0)] = v
                return v
            if flg == 1:
                v = 0
                if (n-1, 0) in dict1.keys():
                    v = dict1[(n-1, 0)] + nums[n]
                else: 
                    # print("here")
                    v = _dfs(n-1,0) ; dict1[(n-1, 0)] = v
                    v = v + nums[n]
                dict1[(n, 1)] = v
                return v
        res = _dfs(length,0) # 这里需要注意 有技巧
        # print(dict1)
        return res
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值