[백준 gold5] 강의실 배정(11000)

이민선(Jasmine)·2023년 8월 25일
0

문제

수강신청의 마스터 김종혜 선생님에게 새로운 과제가 주어졌다.

김종혜 선생님한테는 Si에 시작해서 Ti에 끝나는 N개의 수업이 주어지는데, 최소의 강의실을 사용해서 모든 수업을 가능하게 해야 한다.

참고로, 수업이 끝난 직후에 다음 수업을 시작할 수 있다. (즉, Ti ≤ Sj 일 경우 i 수업과 j 수업은 같이 들을 수 있다.)

수강신청 대충한 게 찔리면, 선생님을 도와드리자!

입력

첫 번째 줄에 N이 주어진다. (1 ≤ N ≤ 200,000)

이후 N개의 줄에 Si, Ti가 주어진다. (0 ≤ Si < Ti ≤ 109)

출력

강의실의 개수를 출력하라.

예제 입력 1

3
1 3
2 4
3 5

예제 출력 1

2

나의 코드

from sys import stdin as s
import heapq

# s = open("input.txt", "rt")  # 주석 처리해야 하는 부분

n = int(s.readline())

lectures = []
for _ in range(n):
    lectures.append(list(map(int, s.readline().split())))
# 시작 시간을 기준으로 정렬. 근데 why?
lectures.sort(key=lambda x: x[0])

room_count = 1
end_times = [lectures.pop(0)[1]]
# 각 강의실의 종료 시각들을 관리할 리스트에서 최소힙을 이용하여 가장 빨리 끝나는 시각을 꺼낼 예정
heapq.heapify(end_times)

for lecture in lectures:
    [new_start_time, new_end_time] = lecture

	# 최소 힙을 이용하여 마지막 수업이 가장 빨리 끝나는 강의실의 강의 종료 시각을 min_end_time에 할당
    min_end_time = heapq.heappop(end_times)
	
    # 새로운 강의 시작 시간이 기존 가장 일찍 끝나는 시각보다 이르면 강의실 개수를 늘리고 end_times 배열에서 종료 시각을 하나 더 관리해야 함.
    if min_end_time > new_start_time:
        room_count += 1
        heapq.heappush(end_times, min_end_time)
	
    # 대소비교 결과와 무관히 원본 heapq에서 꺼냈던 가장 이른 종료 시각을 heapq에 다시 heappush한다.
    heapq.heappush(end_times, new_end_time)

print(room_count)

정렬 기준만 잘 알면 구현하기가 많이 어렵지는 않다. 그런데 정렬 기준이 어렵잖아!!!!

지난 번에 봤던 회의실 배정 문제에서는 종료 시각 기준으로 정렬했었는데, 이번 문제에서는 왜 시작 시각 기준으로 정렬하는 건지 아직 와닿지가 않는다.

처음에 정렬했을 때는

lectures.sort(key=lambda x: (x[1], x[0]))

이렇게 정렬했었고 틀렸었다.
그러다가 hoxy..?하고 x[1]을 지우고 제출해봤는데 맞았다.
근데 아직도 왜 시작 시간 기준으로 정렬하는 건지 논리적으로 설명을 못하겠다.

예전에 풀었던 회의실 배정 문제로 돌아가보자.
https://velog.io/@jasmine0714/%EB%B0%B1%EC%A4%80-silver1-%ED%9A%8C%EC%9D%98%EC%8B%A4-%EB%B0%B0%EC%A0%95-1931
아하 이전 문제는 왜 끝나는 시각 기준으로 정렬했는지 알겠다.
회의실이 1개이고 회의를 최대한 많이 배정해야 하는 문제
-> 빨리 끝내는 애들을 우선 배정해야 타임라인에서 오른쪽에 빈 공간이 많이 생기니까!

근데 이 문제는 대체 왜 시작 시각 기준으로 정렬하는거지?
아 유레카 드디어 이해가 간다.
이 문제의 핵심은 강의와 강의 사이에 텀이 적을수록 효율적이라는 것이다.
강의의 시작시간이 기존 min_end_time과 차이가 얼마 안나야만 텀이 줄어드는 것이다.
그럼 종료시각은? 어차피 min_heap으로 여러 종료시각 중 최소값을 꺼내면 되니까 정렬하여 관리할 필요가 없는 것이다.
이 문제의 관심사는 얼마나 강의와 강의 사이의 텀을 빽빽하게 관리하느냐이기 때문이다.

그리고 처음에 가장 빨리 끝나는 강의시각을 찾으려고 heapq 대신 min을 사용했는데 시간 초과가 났다.
for문 안에서 min을 쓰면 O(N^2)이 되어버리지 뭐야~
그래서 min 대신 heapq로 바꾸니까 시간 복잡도를 O(NlogN)으로 개선할 수 있었다. (heappush와 heappop의 시간복잡도는 각각 logN)

요약: 어차피 종료 시각을 여러개 관리할 거고 그 중에서 최소값을 heapq로 꺼낼거라면, 강의와 강의 사이 텀을 줄이는데 집중해야 한다.

profile
기록에 진심인 개발자 🌿

0개의 댓글