일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 토이 프로젝트
- 코딩테스트
- Spring Framework
- Qoddi
- GitHub
- 테이블 해시 함수
- 와이어 프레임
- maven
- 배포
- LEVEL 2
- 트랜잭션
- couchcoding
- 프로그래머스
- 그리디 알고리즘
- pom.xml
- 백준
- 협업프로젝트
- Spring
- Fun편log
- Java
- 알고리즘
- 6주포트폴리오
- 카우치코딩
- 마법의 엘리베이터
- 유사 칸토어 비트열
- 빌드 툴
- 토이프로젝트
- ERD
- 프로젝트 설계
- DFS
- Today
- Total
소통 하고싶은 개발자
백준 1931 - 회의실 배정 (Java) 본문
문제 개요
풀이
문제에서 원하는 바를 요약하자면 '같은 시간대에 두 개 이상의 회의가 일어나지 않는 조건일 때, 가장 많은 회의를 발생시키려면 몇 개의 회의가 진행될 수 있는지 구하는 프로그램을 작성하라'이다.
우선 백준 카테고리에서 그리디 알고리즘으로 검색했기 때문에 처음부터 그리디 방식을 사용해야겠다 라는 생각으로 문제에 접근했다. (아니었다면 조금 더 헤맸을 것 같다.)
나는 이 문제를 처음 직면하고 세 가지 정도의 생각을 했다.
- 회의 런 타임이 짧은 순서대로 입력받은 회의를 정렬하고 끼워 맞추자.
- 회의 시작 시간을 기준으로 입력받은 회의를 정렬하고 반복문으로 개수를 정하자.
- 회의가 종료되는 시간을 기준으로 입력받은 회의를 정렬하고 반복문으로 개수를 정하자.
가장 처음에 했던 런 타임이 짧은 순서로 정렬을 해야 한다면 정렬 자체는 가능하겠지만, 런 타임이 짧은 기준으로 정렬해서 나열했다고 해서 가장 많은 회의를 발생시킨다는 100 퍼센트의 보장은 절대 할 수 없다고 생각했다.
물론 그리디 알고리즘을 사용한다면 진정한 답이 출력되지 않을 수 있지만, 왠지 너무 답과 달라질 것 같았다. 예를 들어 런 타임이 짧아서 골라온 회의인데 해당 회의를 제거하고 남은 두 개 이상의 회의를 추가할 수 있다면 벌써 맞는 답이 아니기 때문이다. (반례 존재)
두 번째로 시작 시간을 기준으로 정렬하려고 했던 이유는 '각 회의가 겹치지 않도록 정렬할 수 없을까'라는 생각이 밑바탕이 되었기 때문이다. 하지만 예를 들어 첫 회의가 너무 길어서 첫 회의가 아닌 두 번째로 시작되는 회의부터 시작했을 경우 최적의 해가 될 수도 있고, 이런 케이스가 너무 많을 것처럼 생각되었다. (반례 존재)
그래서 고민하다가 세 번째인 종료 시간을 기준으로 정렬하는 것을 떠올린 것이다. 종료 시간이 빠른 순으로 정렬한 후 종료 시간이 가장 빠른 회의부터 가장 늦은 회의까지 반복문을 돌린다면 언제 끝날지 모르는 걱정을 할 필요가 없다. 또한 자동으로 회의가 겹치지 않는 케이스를 추출할 수 있다.
그래서 내가 생각한 이번 문제를 해결하는 코드의 작성 순서는 다음과 같다.
- 먼저 2차원 배열 하나를 만들건데, 이는 각각 [입력받은 회의 순서][회의 시작/종료 시간]을 저장할 배열이다.
- 회의 종료시간을 기준으로 정렬할 건데, 이때 주의할 점이 있다. 당연한 이야기지만 회의 종료시간이 같을 경우 회의 시작시간이 빠른 회의가 우선순위가 된다.
- 이런 예시가 있을 수 있다. (5, 7) 회의가 있고 (7, 7) 회의가 있을 때 (7, 7) 회의가 먼저 진행된다면 (5, 7)의 회의는 진행될 수 없다. 하지만 반대의 경우엔 두 회의 모두 진행될 수 있다.
- 정렬이 완료된 후에 한 차례의 반복문을 통해 이전 회의가 끝나는 시간과 다음 회의가 시작되는 시간을 비교하여 겹친다면 그다음 회의를 비교하는 식의 코드를 작성한다.
코드
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] arg) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int iCount = Integer.parseInt(br.readLine());
int[][] cos = new int[iCount][2];
for (int i=0 ; i<iCount ; i++) {
String[] items = br.readLine().split(" ");
cos[i][0] = Integer.parseInt(items[0]);
cos[i][1] = Integer.parseInt(items[1]);
}
Arrays.sort(cos, (o1, o2) -> {
if (o1[1] == o2[1])
return o1[0]-o1[1];
return o1[1] - o2[1];
});
int count = 0;
int lastEnd = 0;
for (int[] co : cos) {
if (lastEnd > co[0])
continue;
lastEnd = co[1];
count++;
}
System.out.println(count);
return;
}
}
- iCount에 회의의 총개수를, 2차원 배열 변수인 cos에는 입력받은 각 회의별 시작 시간과 종료 시간을 저장한다.
- cos[회의 번호][0] : 회의 시작 시간
- cos[회의 번호][1] : 회의 종료 시간
- 1 회 전체 루프를 통해 시간을 int로 cos에 저장한다.
- Arrays.sort() + Comparator를 사용하여 종료 시간을 우선순위로 두고, 종료 시간이 같다면 시작 시간이 빠른 순서대로 정렬한다.
- 정렬이 완료되면 진행 가능한 회의의 개수(=출력하고자 하는 목표)를 저장할 count와 현재 회의가 종료되는 시간을 의미하는 변수인 lastEnd을 선언한다.
- 반복문에서 이전 회의의 종료 시간은 lastEnd이고 다음 회의가 시작되는 시간은 co[0]으로, 향상된 for 문으로 진행 가능한 회의의 개수를 구한다.
- 현재 인덱스의 회의가 시작될 수 있는 상태라면 lastEnd보다 co[0]이 클 것이고, 그렇다면 현재 인덱스의 회의인 co의 종료시간이 lastEnd에 저장 + count를 증가시켜 주는 것이다.
제출 결과
역시 이전보단 메모리와 시간이 많이 줄긴 했지만 코드 길이는 조금 늘어났다.. 나름 코드 길이를 줄이려고 시도해본 건 비밀은 아니다..^^
지금은 효율이 좋아졌으니 상관없지 싶다!!!!
'알고리즘 풀이 > 백준 알고리즘' 카테고리의 다른 글
백준 2217 - 로프 (Java) (0) | 2022.10.30 |
---|---|
백준 1541 - 잃어버린 괄호 (Java) (0) | 2022.10.29 |
백준 1026 - 보물 (Java) (0) | 2022.10.20 |
백준 11047 - 동전 0 (Java) (0) | 2022.10.10 |
백준 2839 - 설탕 배달 (Java) (1) | 2022.10.08 |