본문 바로가기
코딩테스트 준비/백준

[백준] 17472번 - 다리 만들기 2 [Java]

by mwzz6 2025. 2. 11.

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

 

[백준] 17472번 - 다리 만들기 2 [Java]
[백준] 17472번 - 다리 만들기 2 [Java]
[백준] 17472번 - 다리 만들기 2 [Java]
[백준] 17472번 - 다리 만들기 2 [Java]
[백준] 17472번 - 다리 만들기 2 [Java]


1.  아이디어

 

BFS, DFS 알고리즘으로 각 섬에 대해 번호를 매긴 후 설치할 수 있는 모든 다리를 찾아서 섬은 노드, 다리를 간선으로 하는 최소 스패닝 트리를 구하는 방식으로 해결했다.


2. 문제풀이

 

각 섬에 번호를 붙여서 섬들을 구분하려고 했고 지도를 순회하며 섬을 발견하면 BFS, DFS 알고리즘으로 해당 섬 전체에 번호를 부여했다. 이후 지도를 순회하며 현재 위치가 섬이면서 오른쪽이 바다인 경우, 현재 위치가 섬이면서 아래쪽이 바다인 경우, 다시 섬을 발견할 때까지 해당 방향으로 계속 가며 섬을 발견하면 이 정보로 다리를 생성해서 우선순위 큐에 넣어줬다. 이후 이 우선순위 큐로 크루스칼 알고리즘을 돌려 모든 섬을 연결하는 다리 길이의 최솟값을 찾았다.


3. 코드

 

import java.io.*;
import java.util.*;

public class Main_BFS {

    private static class Edge implements Comparable<Edge> {
        int a;
        int b;
        int w;

        public Edge(int a, int b, int w) {
            this.a = a;
            this.b = b;
            this.w = w;
        }

        @Override
        public int compareTo(Edge o) {
            return Integer.compare(this.w, o.w);
        }
    }

    private static int[] p;

    private static int[] make(int N) {
        int[] arr = new int[1 + N];
        for (int i = 1; i <= N; i++) arr[i] = i;
        return arr;
    }

    private static int find(int x) {
        if (x == p[x]) return x;
        return p[x] = find(p[x]);
    }

    private static void union(int x, int y) {
        p[find(y)] = find(x);
    }

    private static final int[] dr = {-1, 0, 1, 0};
    private static final int[] dc = {0, 1, 0, -1};

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

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        int[][] map = new int[N][M];
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());

            for (int j = 0; j < M; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        // 섬에 번호 붙이기
        boolean[][] visited = new boolean[N][M];
        int num = 1;
        int cnt = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (map[i][j] == 0 || visited[i][j]) continue;

                bfs(i, j, num, N, M, map, visited);
                num++;
                cnt++;
            }
        }

        PriorityQueue<Edge> edges = new PriorityQueue<>();

        // 가로 방향 다리 탐색
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M - 1; j++) {
                if (map[i][j] != 0 && map[i][j + 1] == 0) {
                    int k = 1;
                    while (j + k < M && map[i][j + k] == 0) {
                        k++;
                    }
                    if (j + k != M && k > 2) edges.add(new Edge(map[i][j], map[i][j + k], k - 1));

                    // 탐색 위치 변경
                    j += k - 1;
                }
            }
        }

        // 세로 방향 다리 탐색
        for (int j = 0; j < M; j++) {
            for (int i = 0; i < N - 1; i++) {
                if (map[i][j] != 0 && map[i + 1][j] == 0) {
                    int k = 1;
                    while (i + k < N && map[i + k][j] == 0) {
                        k++;
                    }
                    if (i + k != N && k > 2) edges.add(new Edge(map[i][j], map[i + k][j], k - 1));

                    // 탐색 위치 변경
                    i += k - 1;
                }
            }
        }

        int ans = kruskal(cnt, edges);
        System.out.println(ans);
    }

    private static void bfs(int r, int c, int num, int N, int M, int[][] map, boolean[][] visited) {
        Queue<int[]> q = new ArrayDeque<>();
        q.add(new int[]{r, c});

        visited[r][c] = true;

        while (!q.isEmpty()) {
            int[] node = q.remove();
            map[node[0]][node[1]] = num;

            for (int dir = 0; dir < 4; dir++) {
                int nr = node[0] + dr[dir];
                int nc = node[1] + dc[dir];

                if (nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
                if (map[nr][nc] == 0 || visited[nr][nc]) continue;

                q.add(new int[]{nr, nc});
                visited[nr][nc] = true;
            }
        }
    }

    private static int kruskal(int N, PriorityQueue<Edge> edges) {
        p = make(N);

        int sum = 0;
        int cnt = 0;

        while (!edges.isEmpty()) {
            Edge e = edges.remove();
            if (find(e.a) == find(e.b)) continue;

            union(e.a, e.b);
            sum += e.w;
            cnt++;

            if (cnt == N - 1) return sum;
        }

        return -1;
    }

}
import java.io.*;
import java.util.*;

public class Main_DFS {

    private static class Edge implements Comparable<Edge> {
        int a;
        int b;
        int w;

        public Edge(int a, int b, int w) {
            this.a = a;
            this.b = b;
            this.w = w;
        }

        @Override
        public int compareTo(Edge o) {
            return Integer.compare(this.w, o.w);
        }
    }

    private static int[] p;

    private static int[] make(int N) {
        int[] arr = new int[1 + N];
        for (int i = 1; i <= N; i++) arr[i] = i;
        return arr;
    }

    private static int find(int x) {
        if (x == p[x]) return x;
        return p[x] = find(p[x]);
    }

    private static void union(int x, int y) {
        p[find(y)] = find(x);
    }

    private static final int[] dr = {-1, 0, 1, 0};
    private static final int[] dc = {0, 1, 0, -1};

    private static int N;
    private static int M;
    private static int[][] map;
    private static boolean[][] visited;
    private static int num = 1;

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

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        map = new int[N][M];
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());

            for (int j = 0; j < M; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        // 섬에 번호 붙이기
        visited = new boolean[N][M];
        int cnt = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (map[i][j] == 0 || visited[i][j]) continue;

                dfs(i, j);
                num++;
                cnt++;
            }
        }

        PriorityQueue<Edge> edges = new PriorityQueue<>();

        // 가로 방향 다리 탐색
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M - 1; j++) {
                if (map[i][j] != 0 && map[i][j + 1] == 0) {
                    int k = 1;
                    while (j + k < M && map[i][j + k] == 0) {
                        k++;
                    }
                    if (j + k != M && k > 2) edges.add(new Edge(map[i][j], map[i][j + k], k - 1));

                    // 탐색 위치 변경
                    j += k - 1;
                }
            }
        }

        // 세로 방향 다리 탐색
        for (int j = 0; j < M; j++) {
            for (int i = 0; i < N - 1; i++) {
                if (map[i][j] != 0 && map[i + 1][j] == 0) {
                    int k = 1;
                    while (i + k < N && map[i + k][j] == 0) {
                        k++;
                    }
                    if (i + k != N && k > 2) edges.add(new Edge(map[i][j], map[i + k][j], k - 1));

                    // 탐색 위치 변경
                    i += k - 1;
                }
            }
        }

        int ans = kruskal(cnt, edges);
        System.out.println(ans);
    }

    private static void dfs(int r, int c) {
        map[r][c] = num;
        visited[r][c] = true;

        for (int dir = 0; dir < 4; dir++) {
            int nr = r + dr[dir];
            int nc = c + dc[dir];

            if (nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
            if (map[nr][nc] == 0 || visited[nr][nc]) continue;

            dfs(nr, nc);
        }
    }

    private static int kruskal(int N, PriorityQueue<Edge> edges) {
        p = make(N);

        int sum = 0;
        int cnt = 0;

        while (!edges.isEmpty()) {
            Edge e = edges.remove();
            if (find(e.a) == find(e.b)) continue;

            union(e.a, e.b);
            sum += e.w;
            cnt++;

            if (cnt == N - 1) return sum;
        }

        return -1;
    }

}

4. 후기

 

- BFS

- DFS