• 贪心算法(四) | 加油站、单调递增的数字、监控二叉树 | leecode刷题笔记


    跟随carl代码随想录刷题
    语言:python


    134. 加油站

    题目:在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
    你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
    给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
    👉示例 1:
    输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
    输出: 3
    解释:
    从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
    开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
    开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
    开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
    开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
    开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
    因此,3 可为起始索引。
    👉示例 2:
    输入: gas = [2,3,4], cost = [3,4,3]
    输出: -1
    解释:
    你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
    我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
    开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
    开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
    你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
    因此,无论怎样,你都不可能绕环路行驶一周。

    题目分析

    for循环适合模拟从头到尾的遍历,而while循环适合模拟环形遍历,要善于使用while!

    • 如果gas总量小于消耗总量,那么一定不可能行驶一周。return -1
    • 如果gas总量大于等于消耗总量,那么一定可以行驶一周。
      • 那具体应该以哪个地方作为起点呢?
        • i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,起始位置从i+1算起,再从0计算curSum。

    完整代码如下

    class Solution:
        def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
            start = 0
            curSum = 0
            totalSum = 0
            for i in range(len(gas)):
                curSum += gas[i] - cost[i]
                totalSum += gas[i] - cost[i]
                if curSum < 0:
                    curSum = 0
                    start = i + 1
            if totalSum < 0: return -1
            return start
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    738. 中等单调递增的数字

    题目:当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。
    给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。
    👉示例 1:
    输入: n = 10
    输出: 9
    👉示例 2:
    输入: n = 1234
    输出: 1234
    👉示例 3:
    输入: n = 332
    输出: 299

    题目分析

    先判断题目给定的数字本身是否是单调递增的。

    • 遍历顺序:从后往前遍历:
      • 如果后一位 小于 前一位,那么前一位减1后一位 :之后的位都变成9
    • 整数n变成数组的形式:list(str(n))
    • 计算时转化为数字:int()
    • 返回时转化成数字:int(''.join(num)),注意,num是字符串的形式。

    请添加图片描述

    完整代码如下

    class Solution:
        def monotoneIncreasingDigits(self, n: int) -> int:
            num = list(str(n))
            for i in range(len(num)-1, 0, -1):  # 遍历范围为[len(num)-1, 1],因为range的右边界是开的,所以右边界应该写成`0`
                if int(num[i]) < int(num[i-1]):    
                    num[i-1] = str(int(num[i-1]) - 1)
                    num[i:] = '9' * (len(num) - i)
            return int(''.join(num))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    968. 困难监控二叉树

    题目:给定一个二叉树,我们在树的节点上安装摄像头。
    节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
    计算监控树的所有节点所需的最小摄像头数量。

    题目分析

    难点:

    • 二叉树的遍历
    • 如何隔两个节点放一个摄像头

    遍历顺序:自底向上,从叶子节点的父节点处开始安装摄像头

    • 因此,选择后序遍历
    • 采用递归方式def traversal(root):
      • if not root: return
      • root.left = traversal(root.left) # 中
      • root.right = traversal(root.right) # 右
      • 逻辑处理 # 中
      • return

    如何隔两个节点放一个摄像头

    • 状态转移

    完整代码如下

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def minCameraCover(self, root: Optional[TreeNode]) -> int:
            # Greedy Algo:
            # 从下往上安装摄像头:跳过leaves这样安装数量最少,局部最优 -> 全局最优
            # 先给leaves的父节点安装,然后每隔两层节点安装一个摄像头,直到Head
            # 0: 该节点未覆盖
            # 1: 该节点有摄像头
            # 2: 该节点有覆盖
            
            result = 0
            # 从下往上遍历:后序(左右中)
            def traversal(curr: TreeNode) -> int:
                nonlocal result
                
                if not curr: return 2
                left = traversal(curr.left)
                right = traversal(curr.right)
    
                # Case 1:
                # 左右节点都有覆盖
                if left == 2 and right == 2: 
                    return 0
    
                # Case 2:
                    # left == 0 && right == 0 左右节点无覆盖
                    # left == 1 && right == 0 左节点有摄像头,右节点无覆盖
                    # left == 0 && right == 1 左节点有无覆盖,右节点摄像头
                    # left == 0 && right == 2 左节点无覆盖,右节点覆盖
                    # left == 2 && right == 0 左节点覆盖,右节点无覆盖
                elif left == 0 or right == 0: 
                    result += 1
                    return 1
    
                # Case 3:
                    # left == 1 && right == 2 左节点有摄像头,右节点有覆盖
                    # left == 2 && right == 1 左节点有覆盖,右节点有摄像头
                    # left == 1 && right == 1 左右节点都有摄像头
                elif left == 1 or right == 1:
                    return 2
                
                # 其他情况前段代码均已覆盖
    
            if traversal(root) == 0:
                result += 1
                
            return result
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
  • 相关阅读:
    Abp vNext 依赖注入
    Excel表格如何设置成不可编辑的模式?
    git方面的知识
    富士康推进印度制造的计划倍速,中国制造iPhone占比下滑较快
    迅为RK3588开发板Android12双摄同时显示
    如何清理电脑病毒
    sgu 176 Flow construction (有源汇的上下界最小流)
    神经网络硕士就业前景,图神经网络前景如何
    Java中的JVM是什么?如何调优JVM的性能?
    MindsDB为许多不支持内置机器学习的数据库带来了机器学习功能
  • 原文地址:https://blog.csdn.net/qq_44250700/article/details/126343762