문제
현재 대한민국 해군에 소속되어있는 준하는 문제를 풀기 위해 매일같이 사이버 지식 정보방 통칭 싸지방에 다닌다. 그러나 최근 문제가 생겼다. 싸지방에 사람이 몰려 컴퓨터 수가 모자라게 된 것이다. 이런 사태를 도저히 용납할 수 없었던 준하는 곧 전역하는 선임을 설득해 민원을 넣도록 하는 데 성공했다.
마침내 부대에서는 민원을 받아들이기로 하였고, 컴퓨터를 증설하기로 했다. 또한, 컴퓨터 간의 사용률에 따라 다른 성능의 컴퓨터를 설치하고자 한다.
하지만 예산이 부족해 사람 수 만큼 컴퓨터를 살 수가 없었다. 고심에 고심을 거듭한 준하는 모든 사람이 항상 정해진 시간에 싸지방을 이용한다는 사실을 발견했다.
컴퓨터가 있는 자리에는 1번부터 순서대로 번호가 매겨져 있다. 모든 사람은 싸지방에 들어왔을 때 비어있는 자리 중에서 번호가 가장 작은 자리에 앉는 것이 규칙이다.
준하가 발견한 사실과 이용 규칙을 가지고, 모든 사람이 기다리지 않고 싸지방을 이용할 수 있는 컴퓨터의 최소 개수와 자리별로 몇 명의 사람이 사용했는가를 구하시오.
입력
첫째 줄에 사람의 수를 나타내는 이 주어진다. 둘째 줄부터 개의 줄에 걸쳐서 각 사람의 컴퓨터 이용 시작 시각 와 종료 시각 가 주어진다.
시작 시각이나 종료 시각이 다른 사람과 겹치는 경우는 없다.
출력
첫째 줄에 사람이 모든 사람이 기다리지 않아도 되는 컴퓨터의 최소 개수 를 출력한다.
둘째 줄에는 1번 자리부터 번 자리까지 순서대로 각 자리를 사용한 사람의 수를 띄어쓰기 간격으로 출력한다.
풀이
사용자들의 이용 시간이 주어졌을 때 필요한 컴퓨터 수와 각 컴퓨터의 사용 횟수를 구해야 한다. 시작 시간이 빠른 순서로 사람을 처리하면서, 이미 사용이 끝난 컴퓨터를 다시 쓸 수 있게 관리하면 된다.
코드에서는 먼저 이용 구간을 시작 시간 기준으로 정렬한다. 현재 사용 중인 컴퓨터는 종료 시간과 컴퓨터 번호를 담은 우선순위 큐 pq로 관리하고, 사용이 끝난 컴퓨터 번호는 작은 번호가 먼저 나오도록 별도의 최소 힙 computer에 넣는다.
새 사용자가 들어올 때 종료 시간이 현재 시작 시간 이하인 컴퓨터들을 모두 반환한 뒤, 빈 번호가 있으면 가장 작은 번호를 재사용하고 없으면 새 번호를 만든다. 이렇게 하면 항상 가능한 가장 작은 번호를 배정하면서도 전체 컴퓨터 수와 번호별 사용 횟수를 한 번에 구할 수 있다.
코드
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
int N;
vector<pair<int, int>> v;
int result[100001];
void solve() {
priority_queue<int, vector<int>, greater<int>> computer;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
int idx = 0;
for (int i = 0; i < N; i++) {
while (!pq.empty() && v[i].first >= pq.top().first) {
computer.push(pq.top().second);
pq.pop();
}
if (computer.empty()) {
pq.push({ v[i].second, idx });
result[idx++]++;
}
else {
pq.push({ v[i].second, computer.top() });
result[computer.top()]++;
computer.pop();
}
}
cout << idx << '\n';
for (int i = 0; i < idx; i++)
cout << result[i] << " ";
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> N;
v.resize(N);
for (int i = 0; i < N; i++)
cin >> v[i].first >> v[i].second;
sort(v.begin(), v.end());
solve();
return 0;
}복잡도
- 시간 복잡도: 정렬과 우선순위 큐 연산 때문에 이다.
- 공간 복잡도: 사용 중인 컴퓨터와 빈 번호를 담는 우선순위 큐로 을 사용한다.
마무리
종료된 컴퓨터와 빈 번호를 따로 관리하면, 가장 작은 번호 재사용 규칙까지 자연스럽게 맞출 수 있다.