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

1.题目介绍

题目地址(1353. 最多可以参加的会议数目 - 力扣(LeetCode))

https://leetcode.cn/problems/maximum-number-of-events-that-can-be-attended/

题目描述

给你一个数组 events,其中 events[i] = [startDayi, endDayi] ,表示会议 i 开始于 startDayi ,结束于 endDayi 。

你可以在满足 startDayi <= d <= endDayi 中的任意一天 d 参加会议 i 。在任意一天 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 <= events.length <= 105
  • events[i].length == 2
  • 1 <= startDayi <= endDayi <= 105

2.题解

2.1 贪心算法 + 优先级队列(小根堆)

思路

这里我们思考最优贪心策略:
从左到右遍历时间点i,某会议x在满足起始时间startTime大于i(必要条件,否则无法参加),同时结束时间endTime是最早的(因为它结束时间最早,所以相比那些结束时间晚的更容易被淘汰,故优先选择),每次都选择这样的会议参加或者该时间点没有会议参加,局部最优达成全局最优。

那我们就要考虑进行实现了,首先要维护当前时间点i,可以参加的所有会议(并不是所有会议都开始了)
我们并不想维护一个长度为max_TimeEnd的vector数组,并记录其中所有可以参与的会议(这样我们要遍历max_TimeEnd次数组,而且还要花费非常多的额外空间进行维护)
我们选择使用优先级队列进行动态维护,当当前会议开始后,就将该会议加入到队列中;当当前会议到达结束时间后/我们选择参与了该会议,我们将该会议从队列去除。

还有一个问题,我们该如何根据当前时间i,快速判断有哪些会议开始,又有哪些会议结束呢?
如果使用原来的数组,我们每次选择都要从头到尾遍历一遍并进行判断,这十分的耗费时间,
所以我们选择使用一个哈希表来维护:开始时间--结束时间的映射(这里一个开始时间可能对应多个结束时间,比如像多个会议都是时间i开始,但结束时间不一样,所以第二个参数是vector)
当我们得知当前时间后,就可以用mp.count(i)判断是否有当前日期i开始的会议,并根据映射将mp[i]中存储的结束时间加到优先级队列中,这样就维护了进队操作。

这里我们使用一个基于小根堆的优先级队列,队列存储的是各个会议的结束时间,这样就可以在选择会议时让结束时间早的会议优先出队!
同时对于结束时间 < 当前时间的会议, 说明其已经结束了,不可能再进行选择, 我们也要将其出队。 这样就维护了出队操作

代码

  • 语言支持:C++

C++ Code:

class Solution {
public:
    int maxEvents(vector<vector<int>>& events) {
        unordered_map<int, vector<int>> mp;
        int cnt = 0;
        int max_day = 0; // 用于记录所有活动中最晚的结束时间,确定遍历结束点
        for(vector<int> event : events){
            // 记录开始时间和结束时间的映射,便于下文我们进行寻找
            mp[event[0]].push_back(event[1]);
            max_day = max(max_day, event[1]);
        }

        // 自底向上,将最早(小)结束的排在前面,所以greater(父节点 > 子节点的值就交换,将大的值沉底)
        priority_queue<int, vector<int>, greater<int>> q; 
        // 从左到右遍历每一天,看每一天是否有会议可以参加
        for(int i = 0; i <= max_day; i++){
            // 如果有今天开始的会议,加入优先级队列
            if(mp.count(i)){
                for(int endTime : mp[i]){
                    q.push(endTime);
                }   
            }
            // 去除在今天i已经结束的队列成员
            while(!q.empty() && q.top() < i){
                q.pop();
            }
            // 选择一个结束最早的会议参加
            if(!q.empty()){
                cnt++;
                q.pop();
            }
        }
        return cnt;
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(O(n)\)
  • 空间复杂度:\(O(n)\)
posted @ 2024-06-21 01:52  DawnTraveler  阅读(28)  评论(0编辑  收藏  举报