본문 바로가기

Dot Algo∙ DS/PS

[BOJ] 백준 1931번 회의실 배정 (Java)

    #1931 회의실 배정

    난이도 : 실버 2

    유형 : 그리디

     

    1931번: 회의실 배정

    (1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.

    www.acmicpc.net

    ▸ 문제

    한 개의 회의실이 있는데 이를 사용하고자 하는 N개의 회의에 대하여 회의실 사용표를 만들려고 한다. 각 회의 I에 대해 시작시간과 끝나는 시간이 주어져 있고, 각 회의가 겹치지 않게 하면서 회의실을 사용할 수 있는 회의의 최대 개수를 찾아보자. 단, 회의는 한번 시작하면 중간에 중단될 수 없으며 한 회의가 끝나는 것과 동시에 다음 회의가 시작될 수 있다. 회의의 시작시간과 끝나는 시간이 같을 수도 있다. 이 경우에는 시작하자마자 끝나는 것으로 생각하면 된다.

     입력

    첫째 줄에 회의의 수 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N+1 줄까지 각 회의의 정보가 주어지는데 이것은 공백을 사이에 두고 회의의 시작시간과 끝나는 시간이 주어진다. 시작 시간과 끝나는 시간은 2^31-1보다 작거나 같은 자연수 또는 0이다.

     출력

    첫째 줄에 최대 사용할 수 있는 회의의 최대 개수를 출력한다.

     

    문제 풀이  

    종만북에서 공부헀던 회의실 예약과 같은 유형의 문제이다. 구하고자하는 값은 시간이 겹치지않게 회의실을 최대로 배정하는 것이다. 모든 경우를 탐색하기에는 회의 수가 최대 10만개이기 때문에 최적해를 구하는 그리디 알고리즘으로 접근해봐야 한다. 

     

    그리디 증명

    그리디 알고리즘 개념을 다시 한번 정리해보면 현재의 문제에서 가장 최선인 선택을 도착지까지 매 순간 부분 문제마다 하는 방법이다. 순간 선택에 있어 뒤에 어떤 영향을 미칠지는 고려하지 않는다. 

     

    해당 문제의 탐욕적인 선택 방법은 '현재 진행중인 회의 시간과 겹치지않는 남아있는 팀들 중에서 종료시간이 가장 빠른 회의를 고른다.' 이다. 

     

    이 방법이 맞다면 '종료시간이 가장 빠른 회의를 포함하는 최적해가 존재한다'는 것이 참임을 증명하면 된다. 종료시간이 가장 빠른 회의를 m1, 그보다 느린 회의를 m2라 하자. m1을 포함한 최적해가 존재해야 한다. 만약 m1의 종료시간과  m2의 종료시간 사이에 또 다른 최적해가 겹친다면 그 최적해는 m1을 선택한 경우에 포함이 될 수 있다. 즉, 종료시간이 더 빨리 끝나는 m1을 선택하는 것이 더 늦게 끝나는 회의를 선택하는 것보다 최적해를 구할 확률이 같거나 더 크다는 것을 알 수 있다. 부분 문제에 대한 정당성도 자연스레 성립하게 된다.

     

    m1을 포함하는 최적해가 존재한다.

     

    구현

      1. 회의실 시작시간과 종료시간을 저장하는 객체를 만들어서 데이터를 담는다.Meeting(int start, int end)
      2. 모든 회의실 데이터를 종료시간을 기준으로 오름차순 정렬한다. Collections.sort(list);
        1. 만약 종료시간이 같을 경우 시작시간을 기준으로 오름차순 정렬한다.
      3. 매 순간 가장 종료시간이 빨리 끝나는 회의를 선택한다. 만약 종료시간이 같으면 시작시간이 빠른 회의실을 선택하면 된다. 
        1. if(endTime<=m.start) endTime = m.end;

     

    풀이 코드 

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.StringTokenizer;
    
    public class Main {
    
    	static class Meeting implements Comparable<Meeting>{
    		int start;
    		int end;
    		
    		public Meeting(int start, int end) {
    			this.start =start;
    			this.end = end;
    		}
    
    		@Override
    		public int compareTo(Meeting o) {
    			if(this.end>o.end) return 1;
    			else if(this.end<o.end) return -1;
    			else return this.start-o.start;
    		}
    	}
    	public static void main(String[] args) throws IOException{
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		int n = Integer.parseInt(br.readLine());
    		
    		List<Meeting> list = new ArrayList<>();
    		StringTokenizer st = null;
    		for(int i=0; i<n; i++) {
    			st = new StringTokenizer(br.readLine());
    			int a = Integer.parseInt(st.nextToken());
    			int b = Integer.parseInt(st.nextToken());
    			list.add(new Meeting(a,b));
    		}
    		
    		Collections.sort(list);
    		
    		int endTime=0, cnt=0;
    		for(Meeting m : list) {
    			if(endTime<=m.start) {
    				endTime = m.end;
    				cnt++;
    			}
    		}
    		System.out.println(cnt);
    	}
    }