본문 바로가기

Dot Algo∙ DS/PS

[BOJ] 백준 2665번 미로만들기 (Java)

    #2665 미로만들기

    난이도 : 골드 4

    유형 : BFS / 우선순위 큐

     

     

    2665번: 미로만들기

    첫 줄에는 한 줄에 들어가는 방의 수 n(1 ≤ n ≤ 50)이 주어지고, 다음 n개의 줄의 각 줄마다 0과 1이 이루어진 길이가 n인 수열이 주어진다. 0은 검은 방, 1은 흰 방을 나타낸다.

    www.acmicpc.net

    ▸ 문제

    n×n 바둑판 모양으로 총 n2개의 방이 있다. 일부분은 검은 방이고 나머지는 모두 흰 방이다. 검은 방은 사면이 벽으로 싸여 있어 들어갈 수 없다. 서로 붙어 있는 두 개의 흰 방 사이에는 문이 있어서 지나다닐 수 있다. 윗줄 맨 왼쪽 방은 시작방으로서 항상 흰 방이고, 아랫줄 맨 오른쪽 방은 끝방으로서 역시 흰 방이다.

    시작방에서 출발하여 길을 찾아서 끝방으로 가는 것이 목적인데, 아래 그림의 경우에는 시작방에서 끝 방으로 갈 수가 없다. 부득이 검은 방 몇 개를 흰 방으로 바꾸어야 하는데 되도록 적은 수의 방의 색을 바꾸고 싶다.

     

    아래 그림은 n=8인 경우의 한 예이다.

     

    위 그림에서는 두 개의 검은 방(예를 들어 (4,4)의 방과 (7,8)의 방)을 흰 방으로 바꾸면, 시작방에서 끝방으로 갈 수 있지만, 어느 검은 방 하나만을 흰 방으로 바꾸어서는 불가능하다. 검은 방에서 흰 방으로 바꾸어야 할 최소의 수를 구하는 프로그램을 작성하시오.

    단, 검은 방을 하나도 흰방으로 바꾸지 않아도 되는 경우는 0이 답이다.

     입력

    첫 줄에는 한 줄에 들어가는 방의 수 n(1 ≤ n ≤ 50)이 주어지고, 다음 n개의 줄의 각 줄마다 0과 1이 이루어진 길이가 n인 수열이 주어진다. 0은 검은 방, 1은 흰 방을 나타낸다.

     출력

    첫 줄에 흰 방으로 바꾸어야 할 최소의 검은 방의 수를 출력한다.

     

    문제 풀이  

    (0,0)에서 (n-1, n-1)로 가는 최단거리를 구하는 문제이므로 BFS 탐색을 사용해서 풀이해주면 된다. 한 가지 옵션이 추가되었는데 검은 방을 흰 방으로 최소한으로 바꾸면서 이동하는 것이다.

     

    탐색 방법으로는 두 가지 방법이 있다.

    1. 일반 BFS탐색으로 방문여부 체크를 booelan[x][y][blackToWhite]로 체크해주면서 blackToWhite의 수가 가장 적게 도착한 경로를 선택한다.
    2. 우선순위 큐(priority Queue)로 BFS탐색을 하여 blackToWhite의 수가 낮은 큐가 높은 우선순위를 갖도록 탐색하여 첫 번째로 도착한 경로를 선택한다.

     

    1번은 가장 직관적인 풀이 방법이라 아이디어 내기도 쉽고 구현도 간단하다. 그러나 boolean배열의 크기가 x*y*(x*y)이므로 공간도 많이 차지할 뿐더러 모든 탐색 결과를 비교해줘야하기 때문에 시간도 꽤 걸린다

     

    2번은 우선순위 큐로 한 번의 결과만 반환하면 되기 때문에 시간도 간단하고 boolean배열도 x*y만 선언해주면 되므로 1번의 최적화된 탐색 방식이라고 보면 된다.

     

    설계

    두 방식의 전체적인 설계는 같다. 다만 큐 우선순위와 종료 시점만 살짝 다를 뿐이다.

    1. (x, y, move, blackToWhite)의 초기값 (0,0,0,0)을 큐에 넣어준다.
    2. (n-1, n-1)을 목적지로 BFS 탐색을 시작한다. 
      1. if(map[ny][nx] ==1)  다음 방이 흰 방이라면 단순 move의 수만 +1 해준다.
        1. (nx, ny, move+1, blackToWhite)
      2. if(map[ny][nx] ==0)  다음 방이 검은 방이라면 move의 수와 blackToWhite 둘 다 +1 해준다.
        1. (nx, ny, move+1, blackToWhite+1)
    3. 1번 탐색 경우, min > blackToWhite일 때 min의 값을 갱신해준다.
    4. 2번 탐색 경우, 처음으로 도착한 경로가 최소 blackToWhite이므로 값을 저장 후 탐색을 종료한다.

     

    일반 BFS 풀이 코드 

    import java.io.*;
    import java.util.*;
    
    public class Main {
    	static int[] dx = {-1, 1, 0, 0};
    	static int[] dy = {0, 0, -1, 1};
    	public static void main(String[] args) throws IOException{
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		int n = Integer.parseInt(br.readLine());
    		
    		int[][] map = new int[n][n];
    		for(int i=0; i<n; i++) {
    			String line = br.readLine();
    			for(int j=0; j<line.length(); j++) {
    				map[i][j] = line.charAt(j)-'0';
    			}
    		}
    		bfs(map,n);
    	}
    	
    	static void bfs(int[][] map, int n) {
    		Queue<int[]> q = new LinkedList<>();
    		boolean[][][] check = new boolean[n][n][n*n];
    		q.add(new int[] {0,0,0,0});
    		check[0][0][0] = true;
    		
    		int min = Integer.MAX_VALUE;
    		while(!q.isEmpty()) {
    			int[] p = q.poll();
    			int px = p[0], py=p[1];
    			int mv = p[2], blackToWhite = p[3];
    			
    			if(min < blackToWhite) continue;
    			if(px==n-1 && py==n-1) {
    				if(min > blackToWhite) {
    					min = blackToWhite;
    				}
    				continue;
    			}
    			
    			for(int i=0; i<4; i++) {
    				int nx = px+dx[i];
    				int ny = py+dy[i];
    				
    				if(nx<0 || ny<0 || nx>n-1 || ny>n-1 || check[ny][nx][blackToWhite]) continue;
    				if(map[ny][nx] ==1) {
    					check[ny][nx][blackToWhite] = true;
    					q.add(new int[] {nx, ny, mv+1, blackToWhite});
    				}
    				
    				if(map[ny][nx] ==0) {
    					check[ny][nx][blackToWhite] = true;
    					q.add(new int[] {nx, ny, mv+1, blackToWhite+1});
    				}
    			}
    			
    		}
    		System.out.println(min==Integer.MAX_VALUE? 0 : min);
    	}
    }

     

    BFS + 우선순위 큐 풀이 코드 

    import java.io.*;
    import java.util.*;
    
    public class Main {
    
    	static int[] dx = {-1, 1, 0, 0};
    	static int[] dy = {0, 0, -1, 1};
    	public static void main(String[] args) throws IOException{
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		int n = Integer.parseInt(br.readLine());
    		
    		int[][] map = new int[n][n];
    		for(int i=0; i<n; i++) {
    			String line = br.readLine();
    			for(int j=0; j<line.length(); j++) {
    				map[i][j] = line.charAt(j)-'0';
    			}
    		}
    		bfs(map,n);
    	}
    	
    	static void bfs(int[][] map, int n) {
    		Queue<int[]> q = new PriorityQueue<>((o1,o2) -> (o1[3]-o2[3]));
    		boolean[][] check = new boolean[n][n];
    		q.add(new int[] {0,0,0,0});
    		check[0][0] = true;
    		
    		int answer =0;
    		while(!q.isEmpty()) {
    			int[] p = q.poll();
    			int px = p[0], py=p[1];
    			int mv = p[2], blackToWhite = p[3];
    			if(px==n-1 && py==n-1) {
    				answer = blackToWhite;
    				break;
    			}
    			
    			for(int i=0; i<4; i++) {
    				int nx = px+dx[i];
    				int ny = py+dy[i];
    				if(nx<0 || ny<0 || nx>n-1 || ny>n-1 || check[ny][nx]) continue;
    				check[ny][nx] = true;
    				if(map[ny][nx] ==1) {
    					q.add(new int[] {nx, ny, mv+1, blackToWhite});
    				}
    				else{
    					q.add(new int[] {nx, ny, mv+1, blackToWhite+1});
    				}
    			}
    		}
    		System.out.println(answer);
    	}
    }

     

    실행결과

    일반 BFS 풀이결과
    BFS + 우선순위 큐 풀이결과