백준 2458 키 순서

·2023년 1월 27일
1

문제

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번을 제외한 학생들은 자신의 키가 몇 번째인지 알 수 없다.

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

코드

import java.io.*;
import java.util.*;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        //N, M 입력 받기
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        //그래프 초기화
        boolean[][] graph=new boolean[n][n];
        for(int i=0; i<n; i++)
            graph[i][i]=true;
        
        for(int i=0; i<m; i++){
            st = new StringTokenizer(br.readLine());
            graph[Integer.parseInt(st.nextToken())-1][Integer.parseInt(st.nextToken())-1]=true;
        }

        int answer=0;
        //각각의 노드로 시작해서 정방향 DFS와 역방향 DFS를 순회해서 모든 노드를 방문할 수 있다면 대소관계 비교 가능
        for(int i=0; i<n; i++){
            boolean[] visited=new boolean[n];

            dfs(graph, visited, i);
            dfs2(graph, visited, i);

            if(check(visited))
                answer++;
        }

        System.out.println(answer);
    }

    //모든 노드를 방문 했는지
    public static boolean check(boolean[] visited){
        for(boolean v:visited)
            if(!v)
                return false;

        return true;
    }

    //정방향 DFS
    public static void dfs(boolean[][] graph, boolean[] visited, int node){
        visited[node]=true;

        for(int i=0; i<graph.length; i++){
            if(visited[i])
                continue;

            if(graph[node][i])
                dfs(graph, visited, i);
        }
    }

    //역방향 DFS
    public static void dfs2(boolean[][] graph, boolean[] visited, int node){
        visited[node]=true;

        for(int i=0; i<graph.length; i++){
            if(visited[i])
                continue;

            if(graph[i][node])
                dfs2(graph, visited, i);
        }
    }
}

해결 과정

  1. 정방향으로 가던 역방향으로 가던 한 노드에서 다른 모든 노드를 방문할 수 있다면 대소 관계를 비교할 수 있다. 그렇다면 각 노드에서 다른 모든 노드로의 탐색을 진행해야 한다. Floyd로 탐색하면 찾을 수 있다.

  2. 하지만 그 생각을 못하고 정방향으로 탐색을 진행해보고, 역방향으로 자신으로 오는 노드를 탐색했을 때 모든 노드를 방문한다면 가능한 것으로 판단했다. DFS를 두 번 돌렸다.

  3. 😁

profile
渽晛

0개의 댓글