코딩테스트
[자바] 백준 12764: 싸지방에 간 준하, TreeSet + 우선순위큐 풀이
heemang.dev
2025. 1. 13. 13:43
주의해야 할 부분
사용 가능한 컴퓨터가 여러 대가 있다면 가장 앞에 위치한 컴퓨터를 사용해야 한다. 예를 들어, [2, 4, 5] 번 컴퓨터가 사용 가능하다면 2번 컴퓨터를 사용해야 한다.
문제 풀이
군대를 갔다온 남자라면 싸지방이 무엇인지 알 것이다. 물론 모른다고 해서 못 푸는 문제는 아니다. 싸지방의 크기는 작기 때문에 사용 가능한 컴퓨터는 제한적이다. 일반적으로는 사용 가능한 컴퓨터가 여러 대 있다면 아무 곳이나 앉아서 사용하겠지만, 본 문제에서는 가장 앞에 위치한 컴퓨터를 사용해야 한다고 한다. 따라서 싸지방에 들어왔을 때, (1)사용 중이지 않은 컴퓨터를 확인하고, (2)그 중에서 가장 앞에 위치한 컴퓨터를 사용하면 된다.
- times : N개의 (컴퓨터 사용 시간, 종료 시간)을 저장하되, 사용 시간이 빠른 순으로 저장한다. (우선순위 큐)
- useCount : N개의 컴퓨터 중에서, 각 컴퓨터를 사용한 횟수를 저장한다.
- pcQue : 현재 사용 중인 컴퓨터의 (컴퓨터 번호, 종료 시간)을 저장한다. 단, 종료 시간이 빠른 순으로 저장한다. (우선순위 큐)
- available : 사용 가능한 컴퓨터 번호를 저장한다. 가장 앞에 위치한 컴퓨터부터 사용해야 하므로, 컴퓨터 번호가 작은 순으로 저장한다. 여기서는 TreeSet을 사용하였지만 우선순위 큐를 사용할 수도 있다. 둘 다, 삽입 및 삭제에서 O(log N)의 시간 복잡도를 갖는다.
- set : N명의 사람이 사용한 컴퓨터 번호를 저장한다. set의 특징으로 중복 데이터를 저장하지 않으므로, 최종적으로 어떤 컴퓨터가 사용되었는지 확인할 수 있다.
위 자료구조를 사용하여 코드를 작성하면 된다.
- times에 저장된 데이터를 가져온다. 이 데이터에는 컴퓨터의 (시작 시간, 종료 시간)이 저장되어 있다.
- pcQue를 탐색하면서 사용이 종료된 컴퓨터를 제거한다. 1번에서 구한 컴퓨터 시작 시간을 기준으로 사용이 종료된 컴퓨터를 제거하면 된다.
- 2번에서 제거된 컴퓨터의 번호를 available에 저장한다. available에는 사용 가능한 컴퓨터 번호가 저장되어 있다.
- 사용 가능한 컴퓨터를 찾되 가장 앞에 위치한 컴퓨터를 사용한다. available에는 컴퓨터 번호가 작은 순서대로 저장되어 있으므로, pollFirst()를 통해 가장 앞에 위치한 컴퓨터를 사용하면 된다.
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
PriorityQueue<Time> times = new PriorityQueue<>();
// 컴퓨터 시작-종료 시간 저장
for (int i = 0; i < N; i++) {
String[] inputs = br.readLine().split(" ");
int start = Integer.parseInt(inputs[0]);
int end = Integer.parseInt(inputs[1]);
times.offer(new Time(start, end));
}
int[] useCount = new int[N]; // 컴퓨터별로 사용 횟수
// 사용 중인 컴퓨터의 (컴퓨터 번호, 종료 시간) 저장
// 빨리 종료되는 컴퓨터를 앞에 배치한다.
PriorityQueue<int[]> pcQue = new PriorityQueue<>((p1, p2) -> p1[1] - p2[1]);
// 사용 가능한 컴퓨터 저장
TreeSet<Integer> available = new TreeSet<>();
for (int i = 0; i < N; i++) {
available.add(i);
}
// 싸지방에 필요한 컴퓨터 최소 개수
// 모든 인원이 사용한 컴퓨터 번호를 저장한다.
Set<Integer> set = new HashSet<>();
while (!times.isEmpty()) {
Time current = times.poll(); // 컴퓨터를 사용할 (시작 시간, 종료 시간)
// 종료 시간이 지난 컴퓨터를 모두 반환한다.
while (!pcQue.isEmpty()) {
if (pcQue.peek()[1] > current.start) {
break;
}
int[] poll = pcQue.poll();
available.add(poll[0]); // 사용 가능 상태로 복귀
}
// 사용 가능한 컴퓨터 중 가장 앞의 컴퓨터를 선택
int pickIdx = available.pollFirst(); // 사용 가능한 컵퓨터를 가져오되, 컴퓨터 번호가 가장 작은 것을 가져온다.
useCount[pickIdx]++; // 현재 컴퓨터의 사용 횟수 + 1
pcQue.offer(new int[]{pickIdx, current.end}); // 현재 컴퓨터의 (번호, 종료 시간) 저장
set.add(pickIdx);
}
StringBuilder sb = new StringBuilder();
sb.append(set.size()).append("\n"); // 사용된 컴퓨터의 수
for (int i = 0; i < useCount.length; i++) {
if (useCount[i] == 0) {
break;
}
sb.append(useCount[i]).append(" ");
}
System.out.println(sb);
}
static class Time implements Comparable<Time> {
int start;
int end;
public Time(int s, int e) {
start = s;
end = e;
}
@Override
public int compareTo(Time other) {
return this.start - other.start;
}
}
}
Time 클래스를 사용하지 않고 2차원 배열로도 작성할 수 있다.
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
int[][] times = new int[N][2];
for (int i = 0; i < N; i++) {
String[] inputs = br.readLine().split(" ");
int start = Integer.parseInt(inputs[0]);
int end = Integer.parseInt(inputs[1]);
times[i][0] = start;
times[i][1] = end;
}
Arrays.sort(times, (t1, t2) -> {
return t1[0] - t2[0];
});
PriorityQueue<Integer> available = new PriorityQueue<>();
for (int i = 0; i < N; i++) {
available.add(i);
}
Set<Integer> visited = new HashSet<>();
PriorityQueue<int[]> pcQue = new PriorityQueue<>((p1, p2) -> {
return p1[1] - p2[1];
});
int[] useCounts = new int[N];
for (int i = 0; i < N; i++) {
int[] time = times[i];
while (!pcQue.isEmpty()) {
if (pcQue.peek()[1] > time[0]) {
break;
}
int[] poll = pcQue.poll();
available.offer(poll[0]);
}
int pickIdx = available.poll();
visited.add(pickIdx);
useCounts[pickIdx]++;
pcQue.offer(new int[]{pickIdx, time[1]});
}
StringBuilder sb = new StringBuilder();
sb.append(visited.size()).append("\n");
for (int i = 0; i < N; i++) {
if (useCounts[i] == 0) {
break;
}
sb.append(useCounts[i]).append(" ");
}
System.out.print(sb);
}
}