본문 바로가기
Algorithm

[백준 Gold 3] 2146 다리 만들기 - Java

by Ray 2021. 12. 28.

문제링크 : https://www.acmicpc.net/problem/2146

 

2146번: 다리 만들기

여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다

www.acmicpc.net

 

접근 과정 : 

  • DFS를 통해 같은 대륙은 같은 번호를 붙여서 구분.
  • 대륙을 탐색할 때, 대륙은 Queue에 좌표와 대륙 번호를 저장 (BFS 때 이용하기 위해)
  • 어떤 대륙에서 출발하여, 다른 번호의 대륙을 만날 때, 그 거리를 반환하는 BFS 구성
  • 한칸에서 시작하는 DFS를 하는 것이 아니라, 모든 대륙을 담은 Queue로 한번에 BFS를 시행
  • 방문체크는 int[][]에 비트마스킹을 통해 시행 -> 해당 좌표에 대륙번호가 포함되어 있으면 이미 방문한 곳. 

 

소스 코드 및 결과 :

package BOJ;

/* 
    다리 만들기

*/

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

public class BOJ2146 {

    public static void main(String[] args) throws NumberFormatException, IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        N = Integer.parseInt(br.readLine());
        map = new int[N][N];

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

        numbering();

        int distance = bridge();

        System.out.println(distance);

    }

    static int N;
    static int[][] map;
    static int[][] d = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
    static Queue<int[]> queue;

    static void findIsland(int r, int c, int num) {

        map[r][c] = num;
        queue.add(new int[] { r, c, num });

        for (int i = 0; i < 4; i++) {
            int nr = r + d[i][0];
            int nc = c + d[i][1];

            if (nr < 0 || nr >= N || nc < 0 || nc >= N || map[nr][nc] != 1) {
                continue;
            }

            findIsland(nr, nc, num);
        }

    }

    static void numbering() {
        queue = new LinkedList<>();

        int num = 2;

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (map[i][j] == 1) {
                    findIsland(i, j, num++);
                }
            }
        }
    }

    static int bridge() {

        int[][] visit = new int[N][N];

        int count = 0;

        loop: while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- > 0) {
                int[] now = queue.poll();

           
                for (int i = 0; i < 4; i++) {
                    int nr = now[0] + d[i][0];
                    int nc = now[1] + d[i][1];

                    if (nr < 0 || nr >= N || nc < 0 || nc >= N) {
                        continue;
                    }

                    if (map[nr][nc] == now[2]) {
                        continue;
                    }
                    if ((visit[nr][nc] | (1 << now[2])) == visit[nr][nc]) {
                        continue;
                    }
                    visit[nr][nc] |= 1 << now[2];

                    if (map[nr][nc] != 0) {
                        break loop;
                    }
                    queue.add(new int[] { nr, nc, now[2] });
                }
            }

            count++;
        }

        return count;
    }
}

 

P.S 

대륙 한칸씩 출발점을 정하는 DFS보다는 한번에 시행하는 BFS가 더 빠를 것이라 생각했다.

방문체크가 힘든 부분 (다른 대륙에서 출발했는데 같은 곳을 방문한 경우) 를 비트마스킹을 통해 해결.

다른 좋은 방법이 있다면 어떤 것인지 궁금하다..