Implement a MyCalendarTwo
class to store your events. A new event can be added if adding the event will not cause a triple booking.
Your class will have one method, book(int start, int end)
. Formally, this represents a booking on the half open interval [start, end)
, the range of real numbers x
such that start <= x < end
.
A triple booking happens when three events have some non-empty intersection (ie., there is some time that is common to all 3 events.)
For each call to the method MyCalendar.book
, return true
if the event can be added to the calendar successfully without causing a triple booking. Otherwise, return false
and do not add the event to the calendar.
MyCalendar cal = new MyCalendar();
MyCalendar.book(start, end)
Example 1:
MyCalendar(); MyCalendar.book(10, 20); // returns true MyCalendar.book(50, 60); // returns true MyCalendar.book(10, 40); // returns true MyCalendar.book(5, 15); // returns false MyCalendar.book(5, 10); // returns true MyCalendar.book(25, 55); // returns true
這道題咋一看意思好理解,但是要解出這道題還是需要有條理的思路。
1.首先我們需要定義兩個set<int,int> s1,s2, 來分別存儲沒有交集(該區間被覆蓋一次)、有一層交集(覆蓋兩次)的區間。
2.如果每次預訂的區間與之前的所有區間沒有交集(if(Start>=a.second || End<=a.first)),那麼將其直接加入s1中;如果與map中某個區間有交集,那麼將公共區間加入有一層交集的s2中。
3.最後每次先在s2中查看是否與當前區間有重疊,若有直接返回false,若無則查看s1中是否有與之重疊的區間,若有則更新s2,若無則更新s1。
如下爲代碼實現:
class MyCalendarTwo{
public :
MyCalendarTwo(){};
bool book(int Start,int End){
for(auto a:s2){
if(Start>=a.second || End<=a.first) continue;
else return false;
}
for(auto a:s1){
if(Start>=a.second || End<=a.first) continue;
else s2.insert({max(Start,a.first),min(End,a.second)});
}
s1.insert({Start,End});
return true;
}
private :
set<pair<int,int> > s1,s2;
};
下面將介紹另一種巧妙地方法(以下爲轉載),
解法:建立一個時間點和次數之間的映射,規定遇到起始時間點,次數加1,遇到結束時間點,次數減1。那麼我們首先更改新的起始時間start和結束時間end的映射,start對應值增1,end對應值減1。然後定義一個變量cnt,來統計當前的次數。我們使用map具有自動排序的功能,所以我們遍歷的時候就是按時間順序的,最先遍歷到的一定是一個起始時間,所以加上其映射值,一定是個正數。
舉例:我們現在假設map中已經加入了一個區間[3, 5)了,那麼我們就有下面的映射:
3 -> 1
5 -> -1
假如我們此時要加入的區間爲[6, 8)的話,那麼在遍歷到6的時候,前面經過3和5,分別加1減1,那麼cnt又重置爲0了,而後面的6和8也是分別加1減1,還是0。那麼加入我們新加入的區間爲[3, 8]時,那麼此時的映射爲:
3 -> 2
5 -> -1
8 -> -1
那麼我們最先遍歷到3,cnt爲2,沒有超過3,我們知道此時有兩個事件有重疊,是允許的。然後遍歷5和8,分別減去1,最終又變成0了,始終cnt沒有超過2,所以是符合題意的。如果此時我們再加入一個新的區間[1, 4),那麼此時的映射爲:
1 -> 1
3 -> 2
4 -> -1
5 -> -1
8 -> -1
那麼我們先遍歷到1,cnt爲1,然後遍歷到3,此時cnt爲3了,那麼我們就知道有三個事件有重疊區間了,所以這個新區間是不能加入的,那麼我們要還原其start和end做的操作,把start的映射值減1,end的映射值加1,然後返回false。否則沒有三個事件有共同重疊區間的話,返回true即可,參見代碼如下:
class MyCalendarTwo {
public:
MyCalendarTwo() {}
bool book(int start, int end) {
++freq[start];
--freq[end];
int cnt = 0;
for (auto f : freq) {
cnt += f.second;
if (cnt == 3) {
--freq[start];
++freq[end];
return false;
}
}
return true;
}
private:
map<int, int> freq;
};