코딩테스트/백준

[Java] 백준 1976번 : 여행 가자(BFS, 유니온 파인드 풀이)

이덩우 2023. 12. 30. 02:24

문제소개

 


BFS 풀이

첫 번째 풀이로 BFS를 떠올렸다.

1 -> 2 -> 3 과 같은 경로를 거칠 수 있는지 판단해야하므로 하나의 경로를 나아갈 때 마다 BFS를 통해 도달할 수 있는지 없는지 검사한다.

경로의 수가 많다면 시간초과가 발생할 수 있지만, 주어진 조건으로는 200 * 200 * 1000, 즉 4천만정도여서 시도해봤다.

문제설명이 조금 부실해 여행 경로에 연속된 숫자가 나올 수 있다는 점을 늦게알았지만 해당 조건을 추가해주니 AC를 받았다.

public class Main {

    static int[][] graph;
    static boolean[] visited;
    static String result = "YES";
    static int[] schedule;
    static int N, M;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        N = Integer.parseInt(br.readLine());
        M = Integer.parseInt(br.readLine());
        graph = new int[N + 1][N + 1];
        schedule = new int[M];

        for (int i = 1; i <= N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 1; j <= N; j++) {
                int n = Integer.parseInt(st.nextToken());
                if (n == 1) {
                    graph[i][j] = 1;
                }
            }
        }

        String[] tmp = br.readLine().split(" ");
        for (int i = 0; i < M; i++) {
            schedule[i] = Integer.parseInt(tmp[i]);
        }

        for (int i = 0; i < M - 1; i++) {
            visited = new boolean[N + 1];
            boolean go = bfs(schedule[i], schedule[i + 1]);
            if(schedule[i] == schedule[i + 1]) continue;
            if (!go) {
                result = "NO";
                break;
            }
        }

        System.out.println(result);
    }

    static boolean bfs(int start, int end) {
        Queue<Integer> q = new LinkedList<>();
        q.add(start);

        while (!q.isEmpty()) {
            Integer poll = q.poll();
            visited[poll] = true;
            for (int i = 1; i < N + 1; i++) {
                if (graph[poll][i] == 1 && !visited[i]) {
                    q.add(i);
                    if(i == end) return true;
                }
            }
        }
        return false;
    }
}

 


유니온 파인드 풀이

BFS의 경우 입력 조건이 조금만 까다로워지면 시간초과가 발생할 수 있지만, 유니온 파인드를 활용해 분리 집합 관계를 미리 파악해놓는다면 대량의 여행 경로가 입력으로 들어와도 통과할 수 있다.

find()에서 경로 압축을 위해 지속적으로 자신의 부모 노드값을 갱신하도록 재귀함수를 구성했다.

public class Main {

    static int[][] graph;
    static int[] schedule, parent;
    static int N, M;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        N = Integer.parseInt(br.readLine());
        M = Integer.parseInt(br.readLine());
        graph = new int[N + 1][N + 1];
        schedule = new int[M];
        parent = new int[N + 1];

        for (int i = 1; i <= N; i++) {
            parent[i] = i;
            st = new StringTokenizer(br.readLine());
            for (int j = 1; j <= N; j++) {
                int n = Integer.parseInt(st.nextToken());
                if (n == 1) {
                    graph[i][j] = 1;
                }
            }
        }

        String[] tmp = br.readLine().split(" ");
        for (int i = 0; i < M; i++) {
            schedule[i] = Integer.parseInt(tmp[i]);
        }

        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= N; j++) {
                if (graph[i][j] == 1) {
                    union(i, j);
                }
            }
        }

        for (int i = 0; i < schedule.length - 1; i++) {
            if (find(schedule[i]) != find(schedule[i + 1])) {
                System.out.println("NO");
                return;
            }
        }
        System.out.println("YES");
    }

    static int find(int x) {
        if(x == parent[x]) return x;

        return parent[x] = find(parent[x]);
    }

    static void union(int x, int y) {
        int a = find(x);
        int b = find(y);

        if (a != b) {
            if(a < b) parent[b] = a;
            else parent[a] = b;
        }
    }
}