세탁 예약을 처리하는 최소 기계 수
자바스크립트 코딩테스트 문제로 interval-partition 주제를 연습해보세요. 난이도는 medium이며, 브라우저에서 바로 JavaScript로 풀이를 실행할 수 있습니다.
문제 설명
세탁실 예약 목록 bookings가 주어집니다. 각 예약은 "HH:MM-HH:MM" 형식의 문자열이며, 하나의 기계는 한 번에 하나의 예약만 처리할 수 있습니다.
예약 시작 시각이 어떤 기계의 직전 예약 종료 시각과 같거나 늦으면 그 기계를 다시 사용할 수 있습니다.
모든 예약을 처리하기 위해 필요한 최소 기계 수를 반환하는 solution 함수를 작성하세요.
예를 들어 ['09:00-10:00', '09:20-10:30', '10:00-11:00', '10:10-10:40']라면 어떤 시점에는 동시에 3개의 예약이 겹치므로 정답은 3입니다.
제한사항
bookings의 길이는0이상100,000이하입니다.- 각 예약 문자열은 항상
"HH:MM-HH:MM"형식입니다. - 시각은 같은 날 안에서만 주어집니다.
- 모든 예약은
start < end를 만족합니다. - 입력 순서는 정렬되어 있지 않을 수 있습니다.
- 반환값은 모든 예약을 처리하는 데 필요한 최소 기계 수입니다.
예시
- 입력:
["09:00-10:00", "09:20-10:30", "10:00-11:00", "10:10-10:40"]→ 출력:3 - 입력:
["09:00-09:30", "09:30-10:00", "10:00-10:30"]→ 출력:1 - 입력:
["12:00-12:30", "11:00-11:20", "11:10-12:10", "12:10-12:40"]→ 출력:2 - 입력:
[]→ 출력:0
힌트
- 예약을 먼저 시작 시각 기준으로 정렬해 보세요.
- 지금 사용 중인 기계들 중에서 가장 빨리 비는 기계만 알면 충분합니다.
- 가장 빨리 끝나는 종료 시각을 빠르게 꺼내고 다시 넣을 수 있는 자료구조를 떠올려 보세요.
해설
핵심은 지금 쓰고 있는 기계들 중 가장 빨리 끝나는 예약만 빠르게 확인하면 된다는 점입니다.
풀이 흐름은 다음과 같습니다.
- 모든 예약 문자열을
(start, end)분 단위 숫자로 바꿉니다. - 예약을
start오름차순으로 정렬합니다. - 최소 힙에는 현재 사용 중인 기계들의 종료 시각만 넣습니다.
- 새 예약을 볼 때 힙의 최솟값이 현재 예약의 시작 시각 이하라면, 그 기계는 이미 비었으므로 재사용할 수 있습니다.
- 재사용 가능한 기계가 있으면 힙에서 하나 꺼내고, 현재 예약의 종료 시각을 다시 넣습니다.
- 힙 크기의 최댓값이 곧 필요한 최소 기계 수입니다.
왜 이 방식이 맞을까요?
- 시작 시각이 빠른 예약부터 처리하면, 그 시점까지 필요한 기계 수를 자연스럽게 추적할 수 있습니다.
- 여러 기계 중 무엇을 재사용할지 고민할 필요 없이, 가장 빨리 끝나는 기계만 확인하면 됩니다.
- 그 기계도 아직 안 비었다면 다른 기계들은 더 늦게 비므로 새 기계가 반드시 필요합니다.
예를 들어 09:00-10:00, 09:20-10:30, 10:00-11:00, 10:10-10:40를 순서대로 보면:
09:00-10:00배정 → 기계 1대 사용09:20-10:30배정 → 기존 기계가 아직 안 비어 2대 필요10:00-11:00배정 →10:00에 끝난 기계를 재사용 가능10:10-10:40배정 → 남은 가장 빠른 종료가10:30이라 아직 안 비어 3대 필요
따라서 정답은 3입니다.
function solution(bookings) {
if (bookings.length === 0) return 0;
const times = bookings
.map((booking) => {
const [start, end] = booking.split('-');
return [toMinutes(start), toMinutes(end)];
})
.sort((a, b) => a[0] - b[0]);
const heap = [];
let answer = 0;
for (const [start, end] of times) {
if (heap.length > 0 && heap[0] <= start) {
popMin(heap);
}
pushMin(heap, end);
answer = Math.max(answer, heap.length);
}
return answer;
}
function toMinutes(time) {
const [hh, mm] = time.split(':').map(Number);
return hh * 60 + mm;
}
function pushMin(heap, value) {
heap.push(value);
let index = heap.length - 1;
while (index > 0) {
const parent = Math.floor((index - 1) / 2);
if (heap[parent] <= heap[index]) break;
[heap[parent], heap[index]] = [heap[index], heap[parent]];
index = parent;
}
}
function popMin(heap) {
const min = heap[0];
const last = heap.pop();
if (heap.length > 0) {
heap[0] = last;
let index = 0;
while (true) {
let smallest = index;
const left = index * 2 + 1;
const right = index * 2 + 2;
if (left < heap.length && heap[left] < heap[smallest]) {
smallest = left;
}
if (right < heap.length && heap[right] < heap[smallest]) {
smallest = right;
}
if (smallest === index) break;
[heap[index], heap[smallest]] = [heap[smallest], heap[index]];
index = smallest;
}
}
return min;
}
정렬에 O(n log n), 각 예약 처리에 힙 연산 O(log n)이 들어가므로 전체 시간 복잡도는 O(n log n)입니다.
코드 작성
starter code를 바탕으로 함수를 완성한 뒤 예제 테스트를 실행해보세요.
JavaScript
에디터 로딩 중...
커스텀 테스트
함수 인자를 JSON 배열 형태로 입력하세요. 예: [3, 5], [[1, 2, 3]]
아직 실행하지 않았습니다.
실행 결과
아직 실행하지 않았습니다.
예제 테스트를 실행하면 여기에서 결과를 확인할 수 있습니다.
댓글
문제 풀이 아이디어, 질문, 반례를 자유롭게 나눠보세요.