[BOJ 2458] 키 순서 (Java 풀이)

문제

1번부터 N번까지 번호가 붙여져 있는 학생들에 대하여 두 학생끼리 키를 비교한 결과의 일부가 주어져 있다. 단, N명의 학생들의 키는 모두 다르다고 가정한다. 예를 들어, 6명의 학생들에 대하여 6번만 키를 비교하였고, 그 결과가 다음과 같다고 하자.

  • 1번 학생의 키 < 5번 학생의 키
  • 3번 학생의 키 < 4번 학생의 키
  • 5번 학생의 키 < 4번 학생의 키
  • 4번 학생의 키 < 2번 학생의 키
  • 4번 학생의 키 < 6번 학생의 키
  • 5번 학생의 키 < 2번 학생의 키

이 비교 결과로부터 모든 학생 중에서 키가 가장 작은 학생부터 자신이 몇 번째인지 알 수 있는 학생들도 있고 그렇지 못한 학생들도 있다는 사실을 아래처럼 그림을 그려 쉽게 확인할 수 있다. a번 학생의 키가 b번 학생의 키보다 작다면, a에서 b로 화살표를 그려서 표현하였다.

1번은 5번보다 키가 작고, 5번은 4번보다 작기 때문에, 1번은 4번보다 작게 된다. 그러면 1번, 3번, 5번은 모두 4번보다 작게 된다. 또한 4번은 2번과 6번보다 작기 때문에, 4번 학생은 자기보다 작은 학생이 3명이 있고, 자기보다 큰 학생이 2명이 있게 되어 자신의 키가 몇 번째인지 정확히 알 수 있다. 그러나 4번을 제외한 학생들은 자신의 키가 몇 번째인지 알 수 없다.

학생들의 키를 비교한 결과가 주어질 때, 자신의 키가 몇 번째인지 알 수 있는 학생들이 모두 몇 명인지 계산하여 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 학생들의 수 N (2 ≤ N ≤ 500)과 두 학생 키를 비교한 횟수 M (0 ≤ M ≤ N(N-1)/2)이 주어진다. 다음 M개의 각 줄에는 두 학생의 키를 비교한 결과를 나타내는 두 양의 정수 a와 b가 주어진다. 이는 번호가 a인 학생이 번호가 b인 학생보다 키가 작은 것을 의미한다.

출력

자신이 키가 몇 번째인지 알 수 있는 학생이 모두 몇 명인지를 출력한다.

접근 방법

플로이드-와샬 알고리즘을 적용해서 풀어봅시다.

해당 알고리즘은 O(n^3) 의 시간을 요구합니다. 문제의 요구사항을 보면 학생들의 수는 N (2 <= N <= 500) 이므로 최악의 경우에도 125,000,000 번의 연산이 이루어지므로 충분히 시간내에 가능할 것 같습니다.

  1. int 형 이중 배열인 graph를 생성해 봅시다. 해당 그래프에서 (i, j)가 의미하는 바는 i 와 j 를 비교했을 때 i 가 j 보다 작은 경우라고 가정합니다.

  2. 플로이드-와샬 알고리즘을 적용하기 위해 그래프의 모든 값을 큰 값으로 초기화하고 자기 자신과 비교하는 경우는 없기 때문에 0을 넣어줍니다.

  3. 이제 입력되는 값에 따라서 단방향 그래프를 기입해줍니다. (i, j 를 비교했을때 큰 경우와, 작은 경우가 동시에 존재할 수 없기 때문에 단방향이여야 합니다.)

  4. 플로이드-와샬 알고리즘을 적용해 연결 그래프를 만들어 줍니다. 이제 (a -> b, b -> c == a -> c) 라는 관계를 도식화 할 수 있게 되었습니다.

  5. 1 번부터 n 번 까지 돌면서 (i -> j , j -> i) 중 하나를 만족하는지 확인합니다. 만약 모든 경우에서 만족한다면 result를 1 올려주면 됩니다.

소스 코드

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

public class HeightLevel {

    static int STANDARD = 100000;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] input = br.readLine().split(" ");
        int n = Integer.parseInt(input[0]);
        int m = Integer.parseInt(input[1]);
        int[][] graph = new int[n + 1][n + 1];
        int result = 0;

        for (int i = 0; i <= n; i++) {
            Arrays.fill(graph[i], STANDARD);
            graph[i][i] = 0;
        }

        for (int i = 0; i < m; i++) {
            input = br.readLine().split(" ");
            graph[Integer.parseInt(input[0])][Integer.parseInt(input[1])] = 1;
        }

        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= n; j++) {
                for (int k = 0; k <= n; k++) {
                    graph[j][k] = Math.min(graph[j][k], graph[j][i] + graph[i][k]);
                }
            }
        }

        for (int i = 1; i <= n; i++) {
            if (checkLevel(graph, i, n)) result++;
        }

        System.out.println(result);
        br.close();
    }

    public static boolean checkLevel(int[][] graph, int num, int n) {
        for (int i = 1; i <= n; i++) {
            if (graph[i][num] >= STANDARD && graph[num][i] >= STANDARD) {
                return false;
            }
        }
        return true;
    }
}

결과

좋은 웹페이지 즐겨찾기