백준/Java

[백준 자바] 9372번(상근이의 여행) - BFS(너비 우선 탐색) 사용하기

gamzaggang7 2024. 4. 20. 01:26
728x90

난이도 - 실버 4

문제

상근이는 겨울방학을 맞아 N개국을 여행하면서 자아를 찾기로 마음먹었다. 

하지만 상근이는 새로운 비행기를 무서워하기 때문에, 최대한 적은 종류의 비행기를 타고 국가들을 이동하려고 한다.

이번 방학 동안의 비행 스케줄이 주어졌을 때, 상근이가 가장 적은 종류의 비행기를 타고 모든 국가들을 여행할 수 있도록 도와주자.

상근이가 한 국가에서 다른 국가로 이동할 때 다른 국가를 거쳐 가도(심지어 이미 방문한 국가라도) 된다.

입력

첫 번째 줄에는 테스트 케이스의 수 T(T ≤ 100)가 주어지고,

각 테스트 케이스마다 다음과 같은 정보가 주어진다.

  • 첫 번째 줄에는 국가의 수 N(2 ≤ N ≤ 1 000)과 비행기의 종류 M(1 ≤ M ≤ 10 000) 가 주어진다.
  • 이후 M개의 줄에 a와 b 쌍들이 입력된다. a와 b를 왕복하는 비행기가 있다는 것을 의미한다. (1 ≤ a, b ≤ n; a ≠ b) 
  • 주어지는 비행 스케줄은 항상 연결 그래프를 이룬다.

출력

테스트 케이스마다 한 줄을 출력한다.

  • 상근이가 모든 국가를 여행하기 위해 타야 하는 비행기 종류의 최소 개수를 출력한다.

 


 

728x90

문제를 해결하기 위해 BFS를 사용한다. BFS(너비 우선 탐색)은 정점 v로부터 도달할 수 있는 모든 정점을 발견하기 우해 G의 간선을 체계적으로 탐색하는 알고리즘이다. 너비 우선 탐색을 하기 위해 큐를 사용한다.

  1. 먼저 첫 번째 노드를 큐에 넣고 첫 번째 노드와 인접한 노드들을 방문하며 큐에 저장한다.
  2. 한 입접 리스트가 끝나면 큐에서 노드 하나를 꺼낸다.
  3. 그 노드의 인접 리스트에 있는 노드들을 같은 방법으로 조사한다. 방문된 노드는 무시하고 방문하지 않은 노드는 방문한 뒤 큐에 넣는다.
  4. 큐가 공백이 되면 탐색은 종료된다.

노드는 2차원 ArrayList에 저장하고 방문 리스트는 boolean으로 하였다. 노드 번호와 배열 인덱스를 일치시키기 위해 배열의 크기는 N + 1로 하였다.

import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

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

        int T = Integer.parseInt(br.readLine());

        while (T-- > 0) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int N = Integer.parseInt(st.nextToken());
            int M = Integer.parseInt(st.nextToken());

            ArrayList<Integer>[] planes = new ArrayList[N + 1];
            for (int i = 0; i <= N; i++) {
                planes[i] = new ArrayList<>();
            }

            boolean[] visited = new boolean[N + 1];

            for (int i = 0; i < M; i++) {
                st = new StringTokenizer(br.readLine());
                int a = Integer.parseInt(st.nextToken());
                int b = Integer.parseInt(st.nextToken());
                planes[a].add(b);
                planes[b].add(a); 
            }

            bw.write(String.valueOf(bfs(1, planes, visited)) + '\n');
        }

        bw.flush();
        br.close();
        bw.close();
    }

    private static int bfs(int start, ArrayList<Integer>[] planes, boolean[] visited) {
        int count = 0;

        Queue<Integer> q = new LinkedList<Integer>();

        q.offer(start);
        visited[start] = true;

        while (!q.isEmpty()) {
            int nodeIndex = q.poll();

            for (int i = 0; i < planes[nodeIndex].size(); i++) {
                int temp = planes[nodeIndex].get(i);

                if (!visited[temp]) {
                    visited[temp] = true;
                    count++;
                    q.offer(temp);
                }
            }
        }

        return count;
    }
}

그래프 입력받는 부분에서 양방향으로 연결되도록 하였다. 양방향으로 입력하지 않으면 원하는 결과가 나오지 않는다.

예를 들어 예제 입력 부분에서

2 1
2 3
4 3
4 5

이 값들을 양방향으로 저장하지 않으면

2 -> 1 -> 3

4 -> 3 -> 5

이렇게 저장되는데 시작점을 2로 한다고 해도 1과 3까지만 방문한 채 큐가 비워지기 때문에 2가 출력된다. 이러한 경우를 방지하기 위해 양방향으로 저장한다.

728x90