贪心算法记录 - 下

135. 分发糖果

困难

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目

确定一边之后,再确定另一边

局部最优:只要右边评分比左边大,右边的孩子就多一个糖果。

全局最优:相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果

  • 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
  • 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。

情况一:右比左高(从前向后遍历)

情况二:左比右高(从后向前遍历


⚠️✅ 第二次遍历时,candy[i]的取值要将第一次遍历的结果纳入考虑,取最大值。

python 复制代码
class Solution:
    def candy(self, ratings: List[int]) -> int:
        candy = [1]*len(ratings)
        # 情况一:比较右比左高
        for i  in range(len(ratings)):
            if i > 0 and ratings[i]>ratings[i-1]:
                candy[i] =  candy[i-1]+1
            else:
                candy[i] = 1
        # print(candy)
        # 情况二:比较左比右高
        for i in range(len(ratings)-1, -1, -1):
            if i < len(ratings)-1 and ratings[i] > ratings[i+1]:
                candy[i] = max(candy[i], candy[i+1]+1)
        # print(candy)
        return sum(candy)

860. 柠檬水找零

简单

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false

三种情况:

  • 情况一:账单是5,直接收下。
  • 情况二:账单是10,消耗一个5,增加一个10
  • 情况三:账单是20,优先消耗一个10和一个5,如果不够,再消耗三个5(优先消耗10:美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能!

局部最优:遇到账单20,优先消耗美元10,完成本次找零。

全局最优:完成全部账单的找零。

python 复制代码
class Solution:
    def lemonadeChange(self, bills: List[int]) -> bool:
        five = 0
        ten = 0
        twenty = 0
        
        for bill in bills:
            # 情况一:收到5美元
            if bill == 5:
                five += 1
            
            # 情况二:收到10美元
            if bill == 10:
                if five <= 0:
                    return False
                ten += 1
                five -= 1
            
            # 情况三:收到20美元
            if bill == 20:
                # 先尝试使用10美元和5美元找零
                if five > 0 and ten > 0:
                    five -= 1
                    ten -= 1
                    #twenty += 1
                # 如果无法使用10美元找零,则尝试使用三张5美元找零
                elif five >= 3:
                    five -= 3
                    #twenty += 1
                else:
                    return False
        
        return True

406. 根据身高重建队列

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

to be continued...

452. 用最少数量的箭引爆气球

重叠区间

452. 用最少数量的箭引爆气球

中等

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstartxend之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 x``startx``end, 且满足 xstart ≤ x ≤ x``end,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 points返回引爆所有气球所必须射出的 最小 弓箭数

首先按照左边界将气球排序

判断当前气球的左边界是否小于上一个气球的右边界,小于则可以一起射穿。

当前两个可以一起射穿时,需要更新右边界,判断是否可以和上面的🎈🎈同时射穿。

python 复制代码
class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        if len(points) == 0: return 0
        points.sort(key = lambda x:x[0])
        ans = 1
        for i in range(1,len(points)):
            # 如果左边界大于右边界,则需要更多的一只箭
            if points[i][0] > points[i-1][1]: 
                ans += 1
            else:
                # 重叠则需要更新右边界(上一支箭能射穿的两个气球的右边界的最小值)
                # 判断是否下一个气球是否也能被这支箭射穿
                points[i][1] = min(points[i][1], points[i-1][1])
        return ans

435. 无重叠区间

中等

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠

注意 只在一点上接触的区间是 不重叠的 。例如 [1, 2][2, 3] 是不重叠的。

⚠️✅:intervals[i][1] = min(intervals[i-1][1],intervals[i][1])

更新右边界需要考虑删除一个范围时,删去哪一个:右边界比较大 的那个,这样下一个区间重叠的可能性会变小。于是更新值应该为两个区间右边界的较小值

python 复制代码
class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        intervals.sort(key = lambda x:x[0])
        ans = 0
        for i in range(1,len(intervals)):
            if intervals[i][0] < intervals[i-1][1]:
                ans+=1
                intervals[i][1] = min(intervals[i-1][1],intervals[i][1])
        return ans

56. 合并区间

中等

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

result[-1][1]永远表示最大的右边界

若发现重叠区间,更新最大右边界result[-1][1] = max(result[-1][1], intervals[i][1]):新重叠区间没有被完全包含、被完全包含

没有重叠则加入当前数组(此时result[-1][1]也被更新

python 复制代码
class Solution:
    def merge(self, intervals):
        result = []
        if len(intervals) == 0:
            return result  # 区间集合为空直接返回

        intervals.sort(key=lambda x: x[0])  # 按照区间的左边界进行排序

        result.append(intervals[0])  # 第一个区间可以直接放入结果集中

        for i in range(1, len(intervals)):
            if result[-1][1] >= intervals[i][0]:  # 发现重叠区间
                # 合并区间,只需要更新结果集最后一个区间的右边界,因为根据排序,左边界已经是最小的
                result[-1][1] = max(result[-1][1], intervals[i][1])
            else:
                result.append(intervals[i])  # 区间不重叠

        return result
        

738. 单调递增的数字

中等

当且仅当每个相邻位数上的数字 xy 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增

例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]--,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数。

遍历顺序:从前向后不可以利用前面遍历到的信息,只能从后往前

flag的必要性:1000,不设定flag来标记,按照逻辑变为0900,flag标记了从哪一位开始全部为9.

**flag初始值为len():**例如1234原本就符合题意,不进入处理逻辑。而若flag初始化为0,则全部为9999,不符合题意。

python 复制代码
class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        # 将整数转换为字符串
        strNum = str(N)
        # flag用来标记赋值9从哪里开始
        # 设置为字符串长度,为了防止第二个for循环在flag没有被赋值的情况下执行
        flag = len(strNum)
        
        # 从右往左遍历字符串
        for i in range(len(strNum) - 1, 0, -1):
            # 如果当前字符比前一个字符小,说明需要修改前一个字符
            if strNum[i - 1] > strNum[i]:
                flag = i  # 更新flag的值,记录需要修改的位置
                # 将前一个字符减1,以保证递增性质
                strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + strNum[i:]
        
        # 将flag位置及之后的字符都修改为9,以保证最大的递增数字
        for i in range(flag, len(strNum)):
            strNum = strNum[:i] + '9' + strNum[i + 1:]
        
        # 将最终的字符串转换回整数并返回
        return int(strNum)
python 复制代码
class Solution:
    def monotoneIncreasingDigits(self, N: int) -> int:
        # 将整数转换为字符串
        strNum = list(str(N))

        # 从右往左遍历字符串
        for i in range(len(strNum) - 1, 0, -1):
            # 如果当前字符比前一个字符小,说明需要修改前一个字符
            if strNum[i - 1] > strNum[i]:
                strNum[i - 1] = str(int(strNum[i - 1]) - 1)  # 将前一个字符减1
                # 将修改位置后面的字符都设置为9,因为修改前一个字符可能破坏了递增性质
                for j in range(i, len(strNum)):
                    strNum[j] = '9'

        # 将列表转换为字符串,并将字符串转换为整数并返回
        return int(''.join(strNum))
相关推荐
云卓科技8 分钟前
无人机之任务分配算法篇
科技·算法·机器人·无人机·交互·制造
DaLi Yao11 分钟前
【笔记】复数基础&&复数相乘的物理意义:旋转+缩放
学习·算法·ai·矩阵
云卓科技11 分钟前
无人机之目标检测算法篇
人工智能·科技·算法·目标检测·计算机视觉·机器人·无人机
lzmlzm891 小时前
太阳能板表面缺陷裂缝等识别系统:精准目标定位
算法
陈序缘2 小时前
Rust 力扣 - 238. 除自身以外数组的乘积
开发语言·后端·算法·leetcode·rust
AlexMercer10122 小时前
[C++ 核心编程]笔记 4.2.6 初始化列表
开发语言·数据结构·c++·笔记·算法
zzzhpzhpzzz3 小时前
设计模式——享元模式
算法·设计模式·享元模式
夜雨翦春韭3 小时前
【代码随想录Day54】图论Part06
java·开发语言·数据结构·算法·leetcode·图论
玉树临风ives3 小时前
2024 CSP-J 题解
c++·算法·深度优先·动态规划
hacker7073 小时前
【hacker送书第14期】AI训练师算法与模型训练从入门到精通
人工智能·算法