#2665 미로만들기
난이도 : 골드 4
유형 : BFS / 우선순위 큐
▸ 문제
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 탐색을 사용해서 풀이해주면 된다. 한 가지 옵션이 추가되었는데 검은 방을 흰 방으로 최소한으로 바꾸면서 이동하는 것이다.
탐색 방법으로는 두 가지 방법이 있다.
- 일반 BFS탐색으로 방문여부 체크를 booelan[x][y][blackToWhite]로 체크해주면서 blackToWhite의 수가 가장 적게 도착한 경로를 선택한다.
- 우선순위 큐(priority Queue)로 BFS탐색을 하여 blackToWhite의 수가 낮은 큐가 높은 우선순위를 갖도록 탐색하여 첫 번째로 도착한 경로를 선택한다.
1번은 가장 직관적인 풀이 방법이라 아이디어 내기도 쉽고 구현도 간단하다. 그러나 boolean배열의 크기가 x*y*(x*y)이므로 공간도 많이 차지할 뿐더러 모든 탐색 결과를 비교해줘야하기 때문에 시간도 꽤 걸린다
2번은 우선순위 큐로 한 번의 결과만 반환하면 되기 때문에 시간도 간단하고 boolean배열도 x*y만 선언해주면 되므로 1번의 최적화된 탐색 방식이라고 보면 된다.
설계
두 방식의 전체적인 설계는 같다. 다만 큐 우선순위와 종료 시점만 살짝 다를 뿐이다.
- (x, y, move, blackToWhite)의 초기값 (0,0,0,0)을 큐에 넣어준다.
- (n-1, n-1)을 목적지로 BFS 탐색을 시작한다.
- if(map[ny][nx] ==1) 다음 방이 흰 방이라면 단순 move의 수만 +1 해준다.
- (nx, ny, move+1, blackToWhite)
- if(map[ny][nx] ==0) 다음 방이 검은 방이라면 move의 수와 blackToWhite 둘 다 +1 해준다.
- (nx, ny, move+1, blackToWhite+1)
- if(map[ny][nx] ==1) 다음 방이 흰 방이라면 단순 move의 수만 +1 해준다.
- 1번 탐색 경우, min > blackToWhite일 때 min의 값을 갱신해준다.
- 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);
}
}
실행결과
'Dot Algo∙ DS > PS' 카테고리의 다른 글
[BOJ] 백준 17143번 낚시왕 (Java) (0) | 2021.11.18 |
---|---|
[BOJ] 백준 2668번 숫자고르기 (Java) (0) | 2021.11.17 |
[BOJ] 백준 2473번 세 용액 (Java) (0) | 2021.11.15 |
[BOJ] 백준 1208번 부분수열의 합 2 (Java) (0) | 2021.11.14 |
[BOJ] 백준 2143번 두 배열의 합 (Java) (0) | 2021.11.13 |