알고리즘/백준

1374_강의실

베리영young 2024. 7. 31. 21:36

사용 알고리즘: 그리디 + "우선순위 큐"

사용 언어: java

 

처음에 시간 초과가 난 코드

package week10;

import java.io.*;
import java.util.*;

public class Main_bj_1374_강의실 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		int n = sc.nextInt(); //강의 수
		int[][] arr = new int[n][3];
		
		for (int i = 0; i < arr.length; i++) {
			arr[i][0] = sc.nextInt(); //강의 번호
			arr[i][1] = sc.nextInt(); //강의 시작 시간
			arr[i][2] = sc.nextInt(); //강의 종료 시간
		}
		
		Arrays.sort(arr, (o1, o2) -> {
			if(o1[1] != o2[1]) return o1[1] - o2[1];
			else return o1[2] - o2[2];
		});
//		for(int i=0; i<n; i++) System.out.println(Arrays.toString(arr[i]));
		
		System.out.println(classRoom(n, arr));
	}

	private static int classRoom(int n, int[][] arr) {
		List<Integer> rooms = new ArrayList<>(); //Integer: 강의 끝나는 시간
		
		rooms.add(arr[0][2]); //처음 강의는 집어넣고 시작~
		int cl = 1; //시작한 강의 수
		
		while(cl < n) {
			int startTime = arr[cl][1]; //시작 시간 (list와 비교)
			int endTime = arr[cl++][2]; //끝나는 시간 (list업데이트)
			
			rooms = setRooms(rooms, startTime, endTime);
		}
		
		return rooms.size();
	}

	private static List<Integer> setRooms(List<Integer> rooms, int startTime, int endTime) {
		Collections.sort(rooms); //강의가 빨리 끝나는 순으로 정렬
		
		int earliestEndingLecture = rooms.get(0);
		
		if(earliestEndingLecture <= startTime) { //startTime 전에 빈 강의실이 생기면 그대로 사용
			rooms.set(0, endTime);
		} else { //빈 강의실이 없다면, 강의실 하나 더 만들기
			rooms.add(endTime);			
		}
		
		return rooms;
	}

}

 

 

정답 코드

package week10;

import java.io.*;
import java.util.*;

public class Main_bj_1374_강의실 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		int n = sc.nextInt(); //강의 수
		int[][] arr = new int[n][3];
		
		for (int i = 0; i < arr.length; i++) {
			arr[i][0] = sc.nextInt(); //강의 번호
			arr[i][1] = sc.nextInt(); //강의 시작 시간
			arr[i][2] = sc.nextInt(); //강의 종료 시간
		}
		
		Arrays.sort(arr, (o1, o2) -> {
			if(o1[1] != o2[1]) return o1[1] - o2[1];
			else return o1[2] - o2[2];
		});
//		for(int i=0; i<n; i++) System.out.println(Arrays.toString(arr[i]));
		
		System.out.println(classRoom(n, arr));
	}

	private static int classRoom(int n, int[][] arr) {
		PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> o1 - o2); //빨리 끝나는 순으로 정렬
		
		int cnt = 1; //처음 시작하는 강의는 무조건 교실 하나 배정
		pq.add(arr[0][2]);
		
		for (int room = 1; room < n; room++) {
			int startTime = arr[room][1];
			int endTime = arr[room][2];
			
			//모든 강의실이 다 비어있을 때
			if(pq.isEmpty()) {
				pq.add(endTime);
				continue;
			}
			
			if(pq.peek() > startTime) { //빈 강의실x -> 강의실 하나 더 배정
				cnt++;
			} else { //빈 강의실 만들기
				pq.poll();
			}
			pq.add(endTime); //지금 시작한 수업 pq에 넣기
		}
		
		return cnt;
	}

}

 

 

고찰:

시간 초과가 난 코드와, 정답 코드의 논리는 크게 다를 게 없다.

다만 첫 코드는 우선순위 큐 대신, List만을 가지고 계속 sorting을 해주었다.

 

gpt검색 결과 Collections.sort()와 PriorityQueue 둘 다 시간 복잡도는 O(n log n)이라고 한다.

 

첫 코드를 개선하기 위해 강의실이 빠졌을 때만 sorting을 하게 고친 코드도 있지만, 역시 시간 초과가 났다.

 

결국 n log n의 정렬을 몇 번 수행하느냐가 문제의 핵심이었던 거 같다.

 

 

접근은 거의 다 해놓고 왜 이런 데서 자꾸 아쉬운지... 참...