본문 바로가기
알고리즘 문제풀이/백준

[백준 2146] 다리 만들기

by 선서니 2023. 2. 28.

[문제 바로가기]👇

 

2146번: 다리 만들기

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

www.acmicpc.net

💡풀이💡

한 섬에서 다른 섬으로 최소의 거리를 가진 다리 놓기!

  • 출발하는 섬과 도착하는 섬이 달라야 하고, 이를 판단할 수 있어야 함! >> 섬 별로 번호 붙이기
  • 한 섬에서 출발해서 다른 섬까지 도착하는 최소 거리 재기 >> BFS로 최소 찾기

 

1. 입력 받기

		static int[] dr = {-1,0,1,0}; // 상우하좌
    static int[] dc = {0,1,0,-1}; // 상우하좌
    
    static int N, min;
    static boolean[][] v; // 방문 확인
    static int[][] map; // 지도 정보
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());
        map = new int[N][N];
        
        for(int i=0; i<N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine(), " ");
            for(int j=0; j<N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }
    }

 

2. 섬에 번호 붙이기

출발하는 섬과 도착하는 섬이 달라야 하고, 이를 판단하기 위해 섬에 번호를 붙이기

  • 섬은 상하좌우로 연결되어 있기 때문에 상하좌우 4방향 탐색을 해서 연결되어 있는 곳은 같은 번호 붙이기
  • 한 번의 BFS 탐색이 끝나면(연결 되어 있는 모든 곳을 다 탐색하면) 다음 연결되어 있는 곳을 찾기(다음 섬 찾기)
v = new boolean[N][N];
int num = 2;
for(int i=0; i<N; i++) {
	for(int j=0; j<N; j++) {
	  if(map[i][j]==1) {
      bfsNum(i, j, num);
      num++;
    }
	}
}

static void bfsNum(int i, int j, int num) {
	ArrayDeque<int[]> q = new ArrayDeque<>();
  v[i][j] = true;
  q.offer(new int[] {i,j});
  map[i][j] = num;
        
  while(!q.isEmpty()) {
    int[] pos = q.poll();
            
     for(int d=0; d<4; d++) {
        int ni = pos[0] + dr[d];
        int nj = pos[1] + dc[d];
                
         if(0<=ni&&ni<N && 0<=nj&&nj<N && !v[ni][nj] && map[ni][nj]==1) {
             v[ni][nj] = true;
             q.offer(new int[] {ni,nj});
             map[ni][nj] = num;
        }
     }
   }
}

 

3. 다리 놓는 최소값 찾기

BFS로 한 섬에서 출발해서 섬에 도착할 때까지 BFS의 깊이 구하기

  • BFS의 깊이가 최소 다리 길이가 됨!

이때 BFS 탐색을 시작하는 섬(출발하는 섬)의 번호를 같이 넘겨줘서 도착한 섬이 출발한 섬과 다르면 최소값 구하기

  • 최소값을 구하면 큐에 남아 있는 나머지 경로 탐색을 할 필요가 x
  • queue를 모두 비우고 리턴해서 해당 위치에서의 다리 놓기 탐색 종료
// 다리 놓기 - 다리 최소값 찾기
min = Integer.MAX_VALUE;
for(int i=0; i<N; i++) {
  v = new boolean[N][N];
  for(int j=0; j<N; j++) {
      if(!v[i][j] && map[i][j]>1) {
          bfsBridge(i, j, map[i][j]);
      }
  }
}
System.out.println(min);

static void bfsBridge(int i, int j, int start) {
        ArrayDeque<int[]> q = new ArrayDeque<>();
        v[i][j] = true;
        q.offer(new int[] {i,j});
        
        int size=0, depth=0; // BFS의 깊이가 결국 최소 다리 길이
        while(!q.isEmpty()) {
            size = q.size();
            
            while(size > 0) {
                int[] pos = q.poll();
                
                for(int d=0; d<4; d++) {
                    int ni = pos[0] + dr[d];
                    int nj = pos[1] + dc[d];
                    
                    // 범위 안에 있고, 방문하지 않았으면
                    if(0<=ni&&ni<N && 0<=nj&&nj<N && !v[ni][nj]) {
                        // 섬이고, 출발한 섬과 다른 섬이면
                        if(map[ni][nj] != 0 && start != map[ni][nj] ) {
                            min = Math.min(min, depth);
                            q.clear();
                            return;
                        }
                        else if(map[ni][nj] ==0) { // 바다이면 큐에 추가
                            v[ni][nj] = true;
                            q.offer(new int[] {ni,nj});
                        }
                    }
                }
                size--;
            }
            depth++;
        }
}

 

📌놓쳤던 부분

처음에는 시작하는 섬의 위치를 start 변수에 담고 바다에서 출발해서 다른 섬에 도착하게끔 구현을 했다. 이 경우에 2중 루프로 j가 계속 바뀌기 때문에 바다인데도 섬이라고 생각해서 거리를 계산하는 경우가 있었다. 그래서 출발을 바다가 아니라 섬 일 때만 가능하도록 바꿈

 

잘못된 코드

// 다리 놓기 - 다리 최소값 찾기
min = Integer.MAX_VALUE;
for(int i=0; i<N; i++) {
	int start = 0; // 출발하는 섬 번호 저장
	v = new boolean[N][N];
	for(int j=0; j<N; j++) {
		if(map[i][j]>1) start = map[i][j]; // 섬이면 섬 번호 저장
				
		// 방문하지 않았고, 현재 바다이고, 출발 섬이 바다가 아니면
		if(!v[i][j] && map[i][j]==0 && start!=0) { 
			bfsBridge(i, j, start);
		}
	}
}

 

4. 전체 코드

package M02_4;

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

public class B2146 {
    static int[] dr = {-1,0,1,0}; // 상우하좌
    static int[] dc = {0,1,0,-1}; // 상우하좌
    
    static int N, min;
    static boolean[][] v;
    static int[][] map;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());
        map = new int[N][N];
        
				// 지도 정보 받기
        for(int i=0; i<N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine(), " ");
            for(int j=0; j<N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        
        // 섬 번호 붙이기
        v = new boolean[N][N];
        int num = 2;
        for(int i=0; i<N; i++) {
            for(int j=0; j<N; j++) {
                if(map[i][j]==1) {
                    bfsNum(i, j, num);
                    num++;
                }
            }
        }
        
        // 다리 놓기 - 다리 최소값 찾기
        min = Integer.MAX_VALUE;
        for(int i=0; i<N; i++) {
            v = new boolean[N][N];
            for(int j=0; j<N; j++) {
                if(!v[i][j] && map[i][j]>1) {
                    bfsBridge(i, j, map[i][j]);
                }
            }
        }
        System.out.println(min);
    }
    
    static void bfsNum(int i, int j, int num) {
        ArrayDeque<int[]> q = new ArrayDeque<>();
        v[i][j] = true;
        q.offer(new int[] {i,j});
        map[i][j] = num;
        
        while(!q.isEmpty()) {
            int[] pos = q.poll();
            
            for(int d=0; d<4; d++) {
                int ni = pos[0] + dr[d];
                int nj = pos[1] + dc[d];
                
                if(0<=ni&&ni<N && 0<=nj&&nj<N && !v[ni][nj] && map[ni][nj]==1) {
                    v[ni][nj] = true;
                    q.offer(new int[] {ni,nj});
                    map[ni][nj] = num;
                }
            }
        }
    }
    
    static void bfsBridge(int i, int j, int start) {
        ArrayDeque<int[]> q = new ArrayDeque<>();
        v[i][j] = true;
        q.offer(new int[] {i,j});
        
        int size=0, depth=0;
        while(!q.isEmpty()) {
            size = q.size();
            
            while(size > 0) {
                int[] pos = q.poll();
                
                for(int d=0; d<4; d++) {
                    int ni = pos[0] + dr[d];
                    int nj = pos[1] + dc[d];
                    
                    // 범위 안에 있고, 방문하지 않았으면
                    if(0<=ni&&ni<N && 0<=nj&&nj<N && !v[ni][nj]) {
                        // 섬이고, 출발한 섬과 다른 섬이면
                        if(map[ni][nj] != 0 && start != map[ni][nj] ) {
                            min = Math.min(min, depth);
                            q.clear();
                            return;
                        }
                        else if(map[ni][nj] ==0) { // 바다이면 큐에 추가
                            v[ni][nj] = true;
                            q.offer(new int[] {ni,nj});
                        }
                    }
                }
                size--;
            }
            depth++;
        }
    }
}

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[백준 1149] RGB거리  (0) 2023.03.28
[백준 1541] 잃어버린 괄호  (0) 2023.03.07
[백준 17471] 게리 맨더링  (0) 2023.02.26
[백준 1759] 암호 만들기  (0) 2023.02.24
[백준 1697] 숨바꼭질  (0) 2023.02.24

댓글