목차
문제 : boj1194
필요 알고리즘
- BFS (너비우선 탐색)
- 최단거리를 찾는 문제로 BFS로 풀 수 있다.
- 비트마스킹
- BFS 진행 시 모든 경우에 대해 방문체크가 가능해야한다. 이걸 위해 비트마스킹을 사용한다.
※ 제 코드에서 왜 main 함수에 로직을 직접 작성하지 않았는지, 왜 Scanner를 쓰지 않고 BufferedReader를 사용했는지 등에 대해서는 '자바로 백준 풀 때의 팁 및 주의점' 글을 참고해주세요. 백준을 자바로 풀어보려고 시작하시는 분이나, 백준에서 자바로 풀 때의 팁을 원하시는 분들도 보시는걸 추천드립니다.
풀이
BFS를 모른다면 우선 'BFS 알고리즘 (너비 우선 탐색)'을 보자. 특히 이 문제를 풀기 위해서는 '방문체크에 대해 좀 더 써봄' 부분에 적어놓은걸 이해해야 한다.
이 문제의 경우 새로운 열쇠를 먹은 경우, 기존에 갔던 곳을 다시갈 수 있어야 한다. 예를들어 이하의 예시의 경우 0에서 좌측으로가서 a를 먹은 후, 다시 0이 있던 자리로 돌아갈 수 있어야 1까지 도달 가능하다.
1 4
a0A1
결국엔, 방문체크를 모든 경우를 나타낼 수 있도록 만들수만 있다면 단순 BFS로 풀 수 있는 문제이다. 이 문제의 경우 열쇠를 기준으로 방문체크를 초기화해야 하므로, 새로운 열쇠를 찾았을 때를 기준으로 3차원으로 BFS를 진행하면 된다.
방문체크 v[a][r][c]는 a의 열쇠를 가진 상태로 (r, c)에 방문했음을 나타낸다.
여기서 a는 열쇠가 총 6개이므로 문자열로 처리해도 되지만(예를들어 "110000"이면 a랑 b 열쇠를 가지고 있게), 그 경우 HashMap 등으로 처리해야하므로 3차원 배열로 간단히 처리하려면 비트마스킹을 사용해주면 된다. 이하 코드의 경우, 000000과 같이 6비트가 있다고 했을 때 우측부터 차례대로 a,b,c,d,e,f 열쇠가 있음을 나타낸다. 예를들어 a와 c 열쇠를 가진 상태인 경우 비트로 000101 이 된다.
코드 : github
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
import java.util.StringTokenizer;
class Pos {
int r, c, dist, key;
public Pos(int r, int c, int dist, int key) {
this.r = r;
this.c = c;
this.dist = dist;
this.key = key;
}
}
public class Main {
private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1<<16);
private static final int[] DR = {-1, 0, 0, 1};
private static final int[] DC = {0, 1, -1, 0};
private static final int INF = 2<<6 + 2;
public static void main(String[] args) throws Exception {
new Main().solution();
}
public void solution() throws Exception {
StringTokenizer st = new StringTokenizer(br.readLine());
int r = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());
char[][] map = new char[r][c];
Pos start = null;
for (int i = 0; i < r; i++) {
String row = br.readLine();
for (int j = 0; j < c; j++) {
char cur = row.charAt(j);
if (cur == '0') start = new Pos(i, j, 0, 0);
map[i][j] = cur;
}
}
boolean[][][] v = new boolean[64][r][c];
Queue<Pos> q = new ArrayDeque<>();
int[] qNum = new int[64];
Arrays.fill(qNum, -1);
qNum[0] = 0;
q.add(start);
v[0][start.r][start.c] = true;
while (!q.isEmpty()) {
Pos cur = q.poll();
for (int d = 0; d < 4; d++) {
int nr = cur.r + DR[d];
int nc = cur.c + DC[d];
int dist = cur.dist + 1;
int key = cur.key;
if (nr<0 || nr>=r || nc<0 || nc>=c || v[key][nr][nc] || map[nr][nc]=='#') continue;
if (map[nr][nc] == '1') {
System.out.println(dist);
return;
}
v[key][nr][nc] = true;
char curMap = map[nr][nc];
if (curMap >= 'a' && curMap <= 'f') {
int keyNum = curMap-'a';
if ((key & (1<<keyNum)) == 0) {
q.add(new Pos(nr, nc, dist, key));
key |= 1<<keyNum;
v[key][nr][nc] = true;
q.add(new Pos(nr, nc, dist, key));
continue;
}
}
if (curMap >= 'A' && curMap <= 'F' && (key & (1<<(curMap-'A'))) == 0) {
continue;
}
q.add(new Pos(nr, nc, dist, key));
}
}
System.out.println(-1);
}
}
'PS > BOJ' 카테고리의 다른 글
[자바] 백준 18112 - 이진수 게임 (java) (0) | 2023.03.07 |
---|---|
[자바] 백준 13015 - 별 찍기 - 23 (java) (0) | 2023.03.07 |
[자바] 백준 18513 - 샘터 (java) (2) | 2023.03.03 |
[자바] 백준 1048 - 유니콘 (java) (0) | 2023.03.02 |
[자바] 백준 23807 - 두 단계 최단 경로 3 (java) (0) | 2023.03.01 |
댓글