在问 · 算法题解

1353. 最多可以参加的会议数目

中等栈与堆按开始时间排序 + 小顶堆贪心力扣原题 ↗

题目描述

输入数组 events,events[i] = [startDayi, endDayi] 表示第 i 个会议可以在满足 startDayi <= d <= endDayi 的任意一天 d 参加,参加只需一天。

同一天最多只能参加一个会议。返回最多能参加多少个会议。

示例 1
输入:events = [[1,2],[2,3],[3,4]]
输出:3

解释:第 1、2、3 天分别参加三个会议。

示例 2
输入:events = [[1,2],[2,3],[3,4],[1,2]]
输出:4

解释:两个 [1,2] 安排在第 1、2 天,另两个安排在第 3、4 天。

数据范围

  • 1 <= events.length <= 10^5
  • events[i].length == 2
  • 1 <= startDayi <= endDayi <= 10^5

解题思路:按开始时间排序 + 小顶堆贪心

按天推进。每天把当天开始的会议的结束日放进小顶堆,再丢掉堆中已过期(end < day)的会议。然后参加堆顶那个会议——在所有当前可参加的会议里它最先结束,最“赶时间”,其余的以后还有机会。堆为空时直接跳到下一个会议的开始日,避免逐天空转。

为什么正确

每天选择时,堆中恰好是已开始、未结束、未参加的会议;参加结束日最早的一个不会让答案变差(交换论证)。

复杂度分析

时间 O(n log n)(排序与堆操作,天数不超过会议数加上跳跃次数),空间 O(n)。

易错点

只按结束日排序后贪心选“最早可用的一天”需要额外的并查集或集合;用堆时必须先弹出已过期的会议,否则会参加一个已经结束的会。天数可达 10^5,堆空时要跳天。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def maxEvents(self, events: List[List[int]]) -> int:
        events.sort()
        heap = []
        i = ans = 0
        n = len(events)
        day = 1
        while i < n or heap:
            if not heap:
                day = max(day, events[i][0])
            while i < n and events[i][0] <= day:
                heapq.heappush(heap, events[i][1])
                i += 1
            while heap and heap[0] < day:
                heapq.heappop(heap)
            if heap:
                heapq.heappop(heap)
                ans += 1
            day += 1
        return ans

载入编辑器,自己跑一遍 →

其他解法

堆方法按天推进,每天从已开始的会议中选结束最早的;并查集方法按会议推进,每个会议直接跳到最早的空闲日。两者都基于“结束早的先安排”,只是一个按时间扫、一个按会议扫。

解法 2:按结束时间排序 + 并查集找空闲日 时间 O(n log n + D·α(D)),D 为最大日期,空间 O(D)

同样是“结束早的会议优先安排”的贪心,但换一个方向实现:把会议按结束时间从小到大排序,每个会议安排在不早于开始日的最早空闲日,如果这一天不超过结束日就参加。用并查集维护“从某天起的第一个空闲日”:nxt[d] 初始指向自己;某天被占用后令 nxt[d] = d + 1,find 时沿着指针跳并做路径压缩,就能快速跳过一段连续已占用的日子。

不需要按天模拟,也不需要堆,适合日期范围不太大的情况。需要开一个与最大日期等长的数组,日期范围很大时要先做离散化。

class Solution:
    def maxEvents(self, events: List[List[int]]) -> int:
        events.sort(key=lambda e: e[1])
        last = max(e[1] for e in events)
        nxt = list(range(last + 2))

        def find(x):
            root = x
            while nxt[root] != root:
                root = nxt[root]
            while nxt[x] != root:
                nxt[x], x = root, nxt[x]
            return root

        ans = 0
        for s, e in events:
            d = find(s)
            if d <= e:
                ans += 1
                nxt[d] = d + 1
        return ans

栈与堆 专题的其他题目

查看 栈与堆 全部题目 →