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
DFS(深度优先搜索)是一种常见的图遍历算法,它使用递归或栈的方式,从一个顶点出发,沿着一条路径一直到达最深的节点,然后回溯到上一层继续遍历其他节点。DFS常被用于解决图的连通性问题、路径问题等。在实际应用中,可以使用DFS进行状态搜索、图的遍历、拓扑排序等。 剪枝是指在搜索过程中,通过一系列的策略判断,提前终止当前搜索分支,并跳过一些无用的搜索路径,从而减少搜索时间。剪枝的核心在于提前排除某些明显不符合条件的状态,以减少无效搜索的时间开销,提高效率。在算法设计中,剪枝通常会利用一些特定的性质或条件进行判断,从而缩小搜索空间。 动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划通常用于求解最优化问题,它通过定义状态和状态转移方程,采用自底向上的思路,逐步求解每个子问题的最优值,最终得到原问题的最优解。动态规划的核心是存储已经计算过的子问题的解,避免了重复计算。 贪心算法是一种基于局部最优解的策略,它通过每一步选择在当前状态下最优的解,以期望得到全局最优解。贪心算法的基本思想是由局部最优解推导出全局最优解,通常通过贪心选择性质、最优子结构和贪心选择构成三部分。贪心算法相比其他算法,如动态规划,它的优势在于简单、高效,但缺点在于不能保证获取到全局最优解,只能得到一个近似解。 综上所述,DFS剪枝动态规划和贪心算法算法设计和问题求解中都发挥着重要的作用。具体使用哪种算法取决于问题的性质和要求,需要在实际应用中进行综合考虑和选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值