打家劫舍 (三合一) python 题解

前言

        浅谈一下打家劫舍问题。共有三个打家劫舍问题。一与二差别不大,虽说有动态规划和递归的方法,但其实还是动态规划我感觉(不知道为啥)。三考察的二叉树的操作。

        题目描述

题目(一)打家劫舍(一)_牛客题霸_牛客网 (nowcoder.com)

        你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家;如果偷了第二家,那么就不能偷第一家和第三家。

        给定一个整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。

解题思路:略...我反正没想到

代码:

        递归

class Solution:
    def rob(self, nums: List[int]) -> int:
        ans = 0
        i = 0
        def DFS(ans,i,num=nums):
            if i>=len(nums):
                return ans
            else:
                return max(num[i]+DFS(ans,i+2,nums),DFS(ans,i+1,nums))
        return DFS(ans,i)

        这个代码样例可以通过,但是提交后会超时。应该是因为这递归越到后面就越发散。从下标为0 开始时,会进入计算两个递归的比较最大值,这样就导致了许多重复的计算。

        我们可以开创一个数组,将前n个房子能抢劫到的最多的金额记录下来。第n+1间房子所要判断的就只有前n和前(n-1)加上第n+1间房子能抢到的最大值。这里说的可能不清楚...

动态规划

class Solution:
    def rob(self, nums: List[int]) -> int:
        p = [0 for i in range(105)]
        for i in range(len(nums)):
            p[i+1] = max(p[i],p[i-1]+nums[i])
        return p[len(nums)]

        这个代码给通过了,思路就是从前向后的最大值给求出来。要么是当前一家不偷,这时的值便为在到上一家能得到的最大值,一开始我还感觉不对劲,i 为0 是p[i-1]“应该越界”居然没有报错,结果是p[-1]等于p[105]=0恰好给混过去了。真不错。

         如果用c语言写,可以将循环的数扩大一个 

        for (int i = 1; i <=sizeof(nums); i++){.

        p[i+1] = max(p[i],p[i-1]+nums[i-1]);   // 即p[i] 对应的是前i-1家能抢到的最大的金额,输出p[n+1]就行}

(写的可能不规范...)

题目(二)题解 | #打家劫舍(二)#_牛客博客 (nowcoder.net)

        你是一个经验丰富的小偷,准备偷沿湖的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家,如果偷了第二家,那么就不能偷第一家和第三家。沿湖的房间组成一个闭合的圆形,即第一个房间和最后一个房间视为相邻。

        给定一个长度为n的整数数组nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。

解题思路:和第一个差不多,但是现在要注意第一家拿了钱,最后一家就不能拿钱了。

        可用个啥东西给记录一下。或者直接分类。取了第一家,就将最后一家删去,不选第一家就从第二家开始向后取最大值。

代码:

        递归

class Solution:
    def rob(self, nums: List[int]) -> int:
        def DFS(i=0,ans=0):
            if i>=len(nums):
                return ans
            else:
                return max(DFS(i+1,ans),DFS(i+2,ans+nums[i]))

        if len(nums) == 1: ## 只有一家时为特殊情况,如果舍去最后一家那就没有偷的了,所有直接偷这一家
            return nums[0]
        else:
            a = DFS(1)  ## 从第二家开始偷
            nums.pop(-1) ## 舍去最后一家后,再开始从第一家开始偷,第一家下标开始为零 
            b = DFS()
            return max(a,b)

        这个和上面第一题差不多,能通过大部分测试,但是最后会超时。

动态规划

class Solution:
    def rob(self, nums: List[int]) -> int:
        num1 = nums[:-1]    # 取第一家不取最后一家
        num2 = nums[1:]     # 取最后一家不去第一家
        def dp(num:list[int]):  # 找前i家能取到的最大的金额
            p = [0 for _ in range(len(num)+10)]
            for i in range(len(num)):
                p[i+1] = max(p[i],p[i-1]+num[i])
            return p[len(num)]
        if len(nums) == 1:
            return nums[0]
        else:
            a = dp(num1)
            b = dp(num2)
            return max(a,b)

        还是要去注意特殊只有一个的情况呀,错了两次了QAQ。

题目(三)题解 | #打家劫舍(三)#_牛客博客 (nowcoder.net)

        你是一个经验丰富的小偷,经过上次在街边和湖边得手后你准备挑战一次自己,你发现了一个结构如二叉树的小区,小区内每个房间都存有一定现金,你观察到除了小区入口的房间以外每个房间都有且仅有一个父房间和至多两个子房间。 问,给定一个二叉树结构的小区,如之前两次行动一样,你无法在不触动警报的情况下同时偷窃两个相邻的房间,在不触动警报的情况下最多的偷窃金额。

1.如果输入的二叉树为{2,1,2,#,2,#,1},那么形状结构如下:

        小区入口的房间的值是2 ,偷窃第一层2和第三层 2,1 是最优方案。

2.如果输入的二叉树为{3,2,10},那么形状结构如下:

样例2:小区入口的房间的值是3 ,偷窃第二层 2,10 是最优方案。

        数据范围:二叉树节点数量满足 1 \le n \le 10^5 \1≤n≤105  ,树上的值满足 1 \le nums[i] \le 5000 \1≤nums[i]≤5000 

解题思路

        有输入案例可知,这是一个完全二叉树。其输入为{第一行2^0个,第二行2^1个,第n行2^(n-1)个....}。依旧是尝试下递归法:在每个点的时候都比较选这个点的最大值,和不选这个点的最大值。还要注意即max(a[i]+function(i 的孙一节点)+function(i的孙二节点)+function(i的孙三节点)+function(i的孙四节点), function(i 的子一节点)+function(i 的子二节点)])。

递归代码

# class TreeNode:
#     def __init__(self, x):
#         self.val = x  这个是值
#         self.left = None  这个是TreeNode结构
#         self.right = None  这个也是TreeNode结构
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param root TreeNode类 
# @return int整型
#
class Solution:
    def rob(self , root: TreeNode) -> int:
        # write code here
        def c(root: TreeNode):
            if root==None:
                return 0
            return max(root.val+nc(root.left)+nc(root.right),c(root.left)+c(root.right))
        
        def nc(root:TreeNode):
            if root == None:
                return 0
            return c(root.left)+c(root.right)
        
        return c(root)

  解释:

        函数 c() {‘c’—>‘choose’ : 是否挑选,我确实不会命名...}就是返回不选这个节点情况的最大值。

        函数nc(){'nc'—>'not choose' :一定不选}的建立是为了方便判断子节点是否存在。如果只用一个函数去判断可能有些麻烦。

        轻而易举的我们就能写出这个代码(bushi)。一开始我看好久,就是搞不懂它那个树是怎么存储的。调试了一番后才发现,给的root就是根节点的一个TreeNode类型的数据结构。root.val是这个节点的值,root.left 和root.right 也都是TreeNode类型的数据结构。

        当然这个代码,也一样通过了一半的测试点,后面由于超时过不了。观察代码,我们可以发现,在我们递归的时候,越靠近下方的节点重复计算的次数就越多。

        我们如果可以从最下面的树枝开始计算,逐层向上计算取或不取这个节点所能得到的最大的值,然后向上传结果。那我们就只需要计算一遍就能求出来了。

        先求两个子结点再求根节点。这就和树的后续遍历的执行情况相似。

        后续遍历比较简单,运用到了递归的思想。

                  给定一个根节点root,后序遍历代码如下。

def LRD(root: node):  #给定一个节点类型的数据root
    if not root:   # 如果节点不存在则返回空值
        return None
    LRD(root.left)  # 否则对该节点的左子节点进行后序遍历
    LRD(root.right) # 再对该节点的右子节点进行后续遍历
    print(root.val, end="\t")   #最后输出该节点的值
    return None

        在此代码中,我们总是先“访问”该节点的两个子节点后才访问本节点的值。这性质正好使我们能够将该节点下的情况都算清楚。

        我们稍微修改一下代码,给此函数设置返回值 【a,  b】a 表示偷这家时能得到的最大的金额,b 表示不偷这家能得到的最大的金额。

        显然,如果我们偷了此节点那我们能得到的最大的值是: root.val + LRD( root.left)[ 0 ] + LRD(root.right)[ 0 ]  本节点的值加上不偷左节点的最大的值,加上不偷右节点的最大的值。

        如果我们不偷此节点,注意此时我们不能直接就偷取左右子节点。应为该值不一定是最大的金额。max( LRD( root.left ))+ max ( LRD ( root.right ) )

# class TreeNode:
#     def __init__(self, x):
#         self.val = x  这个是值
#         self.left = None  这个是TreeNode结构
#         self.right = None  这个也是TreeNode结构
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param root TreeNode类 
# @return int整型
#
class Solution:
    def rob(self , root: TreeNode) -> int:
        # write code here
        def abc(root:TreeNode)->list[int]:
            if not root:
                return [0, 0]
            rl = abc(root.left)
            rr = abc(root.right)
            return [root.val+rl[1]+rr[1], max(rl)+max(rr)] 
        
        return max(abc(root))

                代码成功通过。

                hhh时隔一个月终于将这个坑给填上了。

请大哥们批评指点。

在此推荐大家看一下这篇博客。写的真好。(88条消息) 通过列表创建二叉树——python实现_Solarzhou的博客-CSDN博客_python列表生成二叉树

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
OD流水线是一种用于目标检测的深度学习模型,主要用于在图像或视频中识别和定位物体。它通过将输入图像或视频分成小块,并对每个块进行分类和定位,从而实现目标检测的功能。 Python题解是指使用Python编程语言来解决OD流水线中的问题。Python是一种简单易学、功能强大的编程语言,因其易读性和丰富的库而受到广泛应用。 在OD流水线的Python题解中,一般会使用相关的Python库和工具来实现目标检测的各个步骤。首先,需要使用OpenCV或PIL库加载和处理输入的图像或视频数据。然后,需要使用一种深度学习框架,例如TensorFlow或PyTorch,加载预训练的OD模型。接下来,将输入的图像数据传入模型中进行推理,并获取每个目标的类别和位置信息。最后,可以根据需要对检测到的目标进行后续处理,如绘制边界框、标注类别等。 在OD流水线的Python题解中,还可以使用其他辅助工具和技术来提升检测性能。例如,可以使用图像增强技术来提高输入图像的质量,或者使用非极大值抑制算法来去除重叠的边界框。此外,还可以根据具体应用场景进行模型微调或优化,以提高模型在特定数据集上的检测准确率和速度。 总之,OD流水线的Python题解是一种使用Python编程语言实现目标检测的方法。通过使用相关的Python库和工具,结合深度学习模型和其他辅助技术,可以实现高效、精确的目标检测功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值