BOJ/BFS DFS

[백준/BOJ] 18352번 특정 거리의 도시 찾기 (자바/Java)

F12:) 2023. 7. 14. 13:53

https://www.acmicpc.net/problem/18352

 

18352번: 특정 거리의 도시 찾기

첫째 줄에 도시의 개수 N, 도로의 개수 M, 거리 정보 K, 출발 도시의 번호 X가 주어진다. (2 ≤ N ≤ 300,000, 1 ≤ M ≤ 1,000,000, 1 ≤ K ≤ 300,000, 1 ≤ X ≤ N) 둘째 줄부터 M개의 줄에 걸쳐서 두 개

www.acmicpc.net


1. 문제

어떤 나라에는 1번부터 N번까지의 도시와 M개의 단방향 도로가 존재한다. 모든 도로의 거리는 1이다.

이 때 특정한 도시 X로부터 출발하여 도달할 수 있는 모든 도시 중에서, 최단 거리가 정확히 K인 모든 도시들의 번호를 출력하는 프로그램을 작성하시오. 또한 출발 도시 X에서 출발 도시 X로 가는 최단 거리는 항상 0이라고 가정한다.

예를 들어 N=4, K=2, X=1일 때 다음과 같이 그래프가 구성되어 있다고 가정하자.

이 때 1번 도시에서 출발하여 도달할 수 있는 도시 중에서, 최단 거리가 2인 도시는 4번 도시 뿐이다.  2번과 3번 도시의 경우, 최단 거리가 1이기 때문에 출력하지 않는다.

 

입력

첫째 줄에 도시의 개수 N, 도로의 개수 M, 거리 정보 K, 출발 도시의 번호 X가 주어진다. (2 ≤ ≤ 300,000, 1 ≤ ≤ 1,000,000, 1 ≤ ≤ 300,000, 1 ≤ ≤ N) 둘째 줄부터 M개의 줄에 걸쳐서 두 개의 자연수 AB가 공백을 기준으로 구분되어 주어진다. 이는 A번 도시에서 B번 도시로 이동하는 단방향 도로가 존재한다는 의미다. (1 ≤ A≤ N) 단, A와 B는 서로 다른 자연수이다.

출력

X로부터 출발하여 도달할 수 있는 도시 중에서, 최단 거리가 K인 모든 도시의 번호를 한 줄에 하나씩 오름차순으로 출력한다.

이 때 도달할 수 있는 도시 중에서, 최단 거리가 K인 도시가 하나도 존재하지 않으면 -1을 출력한다.

예제 입력 1 

4 4 2 1
1 2
1 3
2 3
2 4

예제 출력 1 

4

예제 입력 2 

4 3 2 1
1 2
1 3
1 4

예제 출력 2 

-1

예제 입력 3 

4 4 1 1
1 2
1 3
2 3
2 4

예제 출력 3 

2
3
 

2. 풀이

이 문제에서의 핵심은 출발점 X로부터 다른 노드까지의 길이를 구해주는 것이다. 그렇다면 이 길이는 어떻게 구해야할까??

 

BFS와 접목하여 생각해보자. BFS에서 다른 노드로 퍼져나가기 위해서는 어떤 노드와 연결된 노드들로 계속 파생되어 나간다. 그렇다면 우리는 처음 시작노드로부터 파생되는 노드까지의 길이는 1이 되고, 그 다음으로 파생되는 길이는 1+1, 그 다음은 1+1+1 ... 

 

그렇다. 우리가 만약 큐를 이용해서 BFS를 구현 중이라면, q.poll()을 했을 때 나오는 값을 s라고 하자. 이 s에서 파생된 모든 노드의 길이는 distance(s) +1 이 된다.

 

그렇게 되면 우리가 어떤 노드 X로부터 모든 노드들의 길이를 구할 수 있게 될 것이다.

 

아래는 BFS의 코드를 응용하여 거리를 구하고, 해당 거리를 출력하는 코드가 된다.

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

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int M = Integer.parseInt(st.nextToken());
        int N = Integer.parseInt(st.nextToken());
        int K = Integer.parseInt(st.nextToken());
        int X = Integer.parseInt(st.nextToken())-1; // 인덱스는 0부터

        Graph graph = new Graph(M);
        for(int i=0; i<N; i++){
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken())-1; // 인덱스는 0부터
            int b = Integer.parseInt(st.nextToken())-1; // 인덱스는 0부터
            graph.addEdge(a, b);
        }
        StringBuilder sb = new StringBuilder();
        int[] distance = graph.BFS(X);
        LinkedList<Integer> res = new LinkedList<>();
        for(int i=0; i<distance.length; i++){
            if(distance[i] == K) res.add(i+1);
        }
        Collections.sort(res); // 오름차순으로 출력해야하므로!!
        for(int i=0; i<res.size(); i++) sb.append(res.get(i)+"\n");
        if(res.isEmpty()) sb.append(-1); // 비어있으면 -1 출력
        System.out.println(sb);
    }
}
class Graph{
    int N;
    LinkedList<Integer>[] adj;

    Graph(int n){
        this.N = n;
        adj = new LinkedList[n];
        for (int i = 0; i < n; i++) adj[i] = new LinkedList<>();
    }

    void addEdge(int a, int b) {adj[a].add(b);}

    int[] BFS(int a){
        int[] distance = new int[N];
        Queue<Integer> q = new LinkedList<>();
        for(int i=0; i<distance.length; i++) distance[i] = -1; // 모든 노드의 길이를 -1로 설정
        q.add(a);
        distance[a] = 0; // 루트 노드의 길이는 0
        while(!q.isEmpty()){
            int now = q.poll();
            ListIterator<Integer> it = adj[now].listIterator();
            while(it.hasNext()){
                int n = it.next();
                if(distance[n] == -1){ // 아직 노드의 길이가 구해지지 않은 상태일 때
                    distance[n] = distance[now]+1;
                    q.add(n);
                }
            }
        }
        return distance;
    }
}

 

3. 결과

 

4. 총평

 - 이 문제를 풀기 전까지 BFS에 대한 고정관념이 박혀, 기존 코드를 바꾸려고 하지 않았다. 하지만 모든 알고리즘은 코드를 응용하는 것이라는 것을 꼭 염두하자.