diff --git a/Python3/253. Meeting Rooms II.md b/Python3/253. Meeting Rooms II.md new file mode 100644 index 0000000..e63f8dd --- /dev/null +++ b/Python3/253. Meeting Rooms II.md @@ -0,0 +1,144 @@ +## Step 1. Initial Solution + +- 前問と同じような構造 + - 誰かがやっていた累積和の方法だとやりやすい? + - メモリを時間分確保するやり方だったか?それだと効率はあまりよくなさそう + - 他に良いやり方を思い出せなかったのでheapを使った処理を元に考える + - 終了時刻を保持しながら次のスタート時間を見ていけば重複数の最大が分かる + +```python +""" +Definition of Interval: +class Interval(object): + def __init__(self, start, end): + self.start = start + self.end = end +""" + +def interval_lt(self, other) -> bool: + if self.start < other.start: + return True + return False + +Interval.__lt__ = interval_lt + +class Solution: + def minMeetingRooms(self, intervals: List[Interval]) -> int: + heapq.heapify(intervals) + required_days = 0 + sorted_end_times = [] + while intervals: + interval = heapq.heappop(intervals) + while sorted_end_times and interval.start >= sorted_end_times[-1]: + sorted_end_times.pop() + sorted_end_times.append(interval.end) + sorted_end_times.sort(reverse=True) + required_days = max(required_days, len(sorted_end_times)) + return required_days +``` + +### Complexity Analysis + +- 時間計算量:O(n^2 log n) + - n回のpopの中に最大nlognのソートが入る + - n < 500なので最大250000*9=2.25*10^6 → 10^8 Steps/s なら 50ms程度 +- 空間計算量:O(n) + +## Step 2. Alternatives + +- AppendからのSortは効率が悪い + - bisect_leftからのinsertの方が良い? + - 以下だとend_indexの位置が間違って取得される + - 数十分かけてもよく分からなかったので一旦置いておく + + ```python + end_index = bisect.bisect(sorted_end_times, interval.end, key=lambda x: -x) + sorted_end_times.insert(end_index, interval.end) + ``` + + - insortでも良い + - こちらはちゃんと動いた + + ```python + bisect.insort(sorted_end_times, interval.end, key=lambda x: -x) + ``` + + - ドキュメントをよく読むと以下のような記述があった + - insort + - `To support inserting records in a table, the *key* function (if any) is applied to *x* for the search step but not for the insertion step.` + - bisect_left + - `To support searching complex records, the key function is not applied to the *x* value.` + - なるほど、検索時にkeyがxに適用されるかされないかの違いがあったのか + - insortを使わない場合は昇順でpop(0)を許容するか、-1倍した値を保持しておくか + + ```python + while sorted_end_times and interval.start >= sorted_end_times[0]: + sorted_end_times.pop(0) + end_index = bisect.bisect(sorted_end_times, interval.end) + sorted_end_times.insert(end_index, interval.end) + ``` + + ```python + while sorted_end_times and interval.start >= -sorted_end_times[-1]: + sorted_end_times.pop() + end_index = bisect.bisect(sorted_end_times, -interval.end) + sorted_end_times.insert(end_index, -interval.end) + ``` + +- https://github.com/tokuhirat/LeetCode/pull/56/files#diff-e176b724e8fbbafc36b447606ee37b1d5b91eca27193941d2c6e233c3c1c28c7R4 + - 累積和はやはりメモリを時間分確保するやり方か + - シンプルだが効率は悪い + - その後で座標圧縮をしているが少し理解が難しかった + - List[List[int]]の展開にsum(intervals, [])を使っているのは初めて見た +- https://github.com/olsen-blue/Arai60/pull/57/files#r2030075157 + - このやり方は確かにありか + - 自分はそもそも累積和を考える時にdict型を使えば良いのでは?と思った + + ```python + class Solution: + def minMeetingRooms(self, intervals: List[Interval]) -> int: + meeting_num_change = defaultdict(int) + for interval in intervals: + meeting_num_change[interval.start] += 1 + meeting_num_change[interval.end] -= 1 + + required_days = 0 + num_meetings = 0 + for change_time in sorted(meeting_num_change.keys()): + num_meetings += meeting_num_change[change_time] + required_days = max(required_days, num_meetings) + return required_days + ``` + +- [https://github.com/shining-ai/leetcode/blob/main/arai60/54-60_others/56_253_Meeting Rooms II/level_5.py](https://github.com/shining-ai/leetcode/blob/main/arai60/54-60_others/56_253_Meeting%20Rooms%20II/level_5.py) + - 終了時間だけを管理しておく方法 + - 新しく始める時に前のミーティングが終わっていたら使うみたいなイメージのよう + - このイメージだとミーティング数を数えるのは最後のstartの後だけで済むのか + +## Step 3. Final Solution + +- 元の実装の改良版で落ち着いた + +```python +import bisect + +def interval_lt(self, other): + if self.start < other.start: + return True + return False + +Interval.__lt__ = interval_lt + +class Solution: + def minMeetingRooms(self, intervals: List[Interval]) -> int: + heapq.heapify(intervals) + end_times = [] + required_days = 0 + while intervals: + interval = heapq.heappop(intervals) + while end_times and end_times[-1] <= interval.start: + end_times.pop() + bisect.insort(end_times, interval.end, key=lambda x: -x) + required_days = max(required_days, len(end_times)) + return required_days +```