Programming/Algorithm

[Java] 백준 1931 회의실 배정

Yejii 2021. 3. 24. 01:47

 

문제

회의의 시작시간, 종료시간이 주어질 때 사용할 수 있는 회의의 최대개수를 출력하는 문제.

 

 

풀이 

- 처음엔 문제를 잘못 이해했다.

   - 시작시간이 2 종료시간이 2라고 할 때 시작과 동시에 끝나므로 해당 경우도 갯수에 더해줘야 한다.

   - 또한 (1 2) (2 2) 인 경우나 (2 2) (2 5) 인 경우 2 회의 모두 결과값에 포함될 수 있다.

- 첫번째로 푼 풀이는 다이나믹 프로그래밍이었다.

   - 현재 인덱스에 대해서 이전의 인덱스를 모두 확인하여 최댓값을 구하게끔 했다.

   - 이 경우 O(n^2)이라는 시간 복잡도가 나와 N이 10만인 경우 100억의 연산이 필요하다는 문제가 있었다. 당연히 시간초과가 났다. 

- 두번째로 푼 풀이는 그리디였다.

    - 사실 회의를 최대한으로 하는 횟수는 매우 간단하게 구할 수 있다.

    - 회의가 빨리 끝나는 것을 기준으로 정렬한 뒤에 0번째 회의부터 더해나가는 것이다.

    - 이 로직을 사용하면 O(N)에 풀이할 수 있다.

 

 

 

코드

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class b1931 {

    static class Schedule {
        int start, end;

        public Schedule(int start, int end) {
            this.start = start;
            this.end = end;
        }

        @Override
        public String toString() {
            return "Schedule{" +
                    "start=" + start +
                    ", end=" + end +
                    '}';
        }
    }

    public static void main(String[] args) throws IOException {
//        wrongAnswer();
        solution2();
    }

    /* 그리디 [8:00~]
     - 끝나는 시간을 기준으로 오름차순 정렬한 뒤에
     - 차례로 구하면 탐욕법으로 최대 개수를 구할 수 있다.

     - 다음과 같은 경우 답은 3이다.
    3
    2 2
    1 2
    2 3
    */
    static void solution2() throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());
        ArrayList<Schedule> list = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            list.add(new Schedule(start, end));
        }
        Collections.sort(list, new Comparator<Schedule>() {
            @Override
            public int compare(Schedule o1, Schedule o2) {
                if (o1.end != o2.end)
                    return o1.end > o2.end ? 1 : o1.end == o2.end ? 0 : -1;
                return o1.start > o2.start ? 1 : o1.start == o2.start ? 0 : -1;
            }
        });

        int cnt = 1;
        int prev_end = list.get(0).end;
        for (int i = 1; i < N; i++) {
            if (prev_end <= list.get(i).start) {
                cnt++;
                prev_end = list.get(i).end;
            }
        }

        System.out.println(cnt);
    }

    // 다이나믹 프로그래밍 풀이 : 시간초과
    // - 현재 배열방에 대해서 그 전 배열방을 모두 탐색하며 최솟값을 찾아간다.
    // - O(N^2) => O(100_000 * 100_000) => O(10_000_000_000) 100억
    static void wrongAnswer() throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());

        ArrayList<Schedule> list = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            list.add(new Schedule(start, end));
        }
        Collections.sort(list, new Comparator<Schedule>() {
            @Override
            public int compare(Schedule o1, Schedule o2) {
                if (o1.start == o2.start) {
                    return o1.end > o2.end ? 1 : (o1.end == o2.end ? 0 : -1);
                }
                return o1.start > o2.start ? 1 : -1;
            }
        });

        int[] dp = new int[N]; // N번째 회의까지 최대 개수
        Arrays.fill(dp, 1);
        for (int i = 1; i < N; i++) { // 기준
            for (int j = i - 1; j >= 0; j--) { // 탐색 인덱스
                int nextStart = list.get(i).start;
                int prevStart = list.get(j).start;
                int prevEnd = list.get(j).end;
                if (dp[i] <= dp[j]
                        && nextStart >= prevEnd && prevStart != prevEnd) {
                    dp[i] = dp[j] + 1;
                }
            }
        }

        System.out.println(Arrays.stream(dp).max().getAsInt());
    }
}