力扣-1353. 最多可以參加的會議數目

DawnTraveler發表於2024-06-21

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)\)

相關文章