1. 문제
https://www.acmicpc.net/problem/13460
2. 풀이 과정
N x M 크기의 게임판이 주어지며, 각 영역은 빨간구슬(R), 파란구슬(B), 빈칸(.), 벽(#), 구멍(O)으로 구분된다.
빨간구슬 파란구슬 각각 1개 씩 주어지며, 가장 바깥 행과 열이 벽으로 막혀있다.
이 때, 게임판을 상,하,좌,우로 기울이며, 구슬은 벽을 만날 때까지 이동한다.
게임판을 몇 번을 기울여야 빨간구슬을 구명(O)에 넣을 수 있는 지 횟수를 구하는 문제이다.
단, 파란 구슬이 구멍에 들어가면 실패이고, 빨간구슬과 파란구슬이 동시에 구멍에 들어가도 실패이다.
또한, 10회 이상 시도해도 실패이다.
실패는 -1을 반환한다.
문제 접근
어려운 문제였고, 이 문제는 BFS를 통해 최단 거리를 구하는 문제이다.
고려할 사항이 많아서 머리가 아팠다..
1. 두 구슬을 모두 굴린다.
- 다른 문제와 달리, 구슬이 벽을 만날 때까지 이동하므로 그 부분을 신경써줘야 한다.
- 한 방향을 지정해서 벽을 만날때까지 굴린다.
- 구슬이 구멍을 만나는 경우를 고려한다.
- dist는 구슬의 이동거리로 두 구슬이 같은 영역에 있을 경우를 처리하기 위해 카운트한다.
MoveResult r = move(cur.rx, cur.ry, i);
MoveResult b = move(cur.bx, cur.by, i);
private static MoveResult move(int x, int y, int dir) {
int dist = 0;
while (true) {
int nx = x + dx[dir];
int ny = y + dy[dir];
if (map[nx][ny] == '#') {
break;
}
x = nx;
y = ny;
dist++;
if (map[x][y] == 'O') {
return new MoveResult(x, y, dist, true);
}
}
return new MoveResult(x, y, dist, false);
}
2. 구슬을 굴린 결과에 따라 결과 반환
- 파란 구슬이 구멍에 들어가면 무조건 실패이다. => 다른 방향 탐색을 시작한다.
- 빨간 구슬만 들어간 경우 결과(굴린 횟수)를 리턴한다.
// 파란 구슬이 먼저 빠지면 실패
if (b.inHole) {
continue;
}
// 빨간 구슬만 빠지면 성공
if (r.inHole) {
return cur.depth + 1;
}
3. 같은 영역에 두 구슬이 들어간 경우
- 빨간 구슬, 파란 구슬이 같은 영역에 있을 수 없으므로 빨간 구슬, 파란 구슬이 같은 영역에 있는 케이스를 고려해줘야 한다.
- 같은 영역에 있을 경우 앞에서 구한 dist를 가지고 구슬의 위치를 조정해준다.
// 같은 위치면 더 많이 이동한 쪽을 한 칸 뒤로
if (r.x == b.x && r.y == b.y) {
if (r.dist > b.dist) {
r.x -= dx[i];
r.y -= dy[i];
} else {
b.x -= dx[i];
b.y -= dy[i];
}
}
3. 코드
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
// 북, 동, 남, 서
static int[] dx = {-1, 0, 1, 0};
static int[] dy = {0, 1, 0, -1};
static int N;
static int M;
static char[][] map;
static boolean[][][][] visited;
public static void main(String[] args) throws IOException {
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
map = new char[N][M];
int rx = 0, ry = 0, bx = 0, by = 0;
for (int i = 0; i < N; i++) {
String line = br.readLine();
for (int j = 0; j < M; j++) {
map[i][j] = line.charAt(j);
if (map[i][j] == 'R') {
rx = i;
ry = j;
} else if (map[i][j] == 'B') {
bx = i;
by = j;
}
}
}
int result = bfs(rx, ry, bx, by);
bw.write(result + "\n");
bw.flush();
bw.close();
}
private static int bfs(int rx, int ry, int bx, int by) {
visited = new boolean[N][M][N][M];
Queue<State> q = new LinkedList<>();
q.offer(new State(rx, ry, bx, by, 0));
visited[rx][ry][bx][by] = true;
while (!q.isEmpty()) {
State cur = q.poll();
if (cur.depth >= 10) {
continue;
}
for (int i = 0; i < 4; i++) {
MoveResult r = move(cur.rx, cur.ry, i);
MoveResult b = move(cur.bx, cur.by, i);
// 파란 구슬이 먼저 빠지면 실패
if (b.inHole) {
continue;
}
// 빨간 구슬만 빠지면 성공
if (r.inHole) {
return cur.depth + 1;
}
// 같은 위치면 더 많이 이동한 쪽을 한 칸 뒤로
if (r.x == b.x && r.y == b.y) {
if (r.dist > b.dist) {
r.x -= dx[i];
r.y -= dy[i];
} else {
b.x -= dx[i];
b.y -= dy[i];
}
}
if (!visited[r.x][r.y][b.x][b.y]) {
visited[r.x][r.y][b.x][b.y] = true;
q.offer(new State(r.x, r.y, b.x, b.y, cur.depth + 1));
}
}
}
return -1;
}
private static MoveResult move(int x, int y, int dir) {
int dist = 0;
while (true) {
int nx = x + dx[dir];
int ny = y + dy[dir];
if (map[nx][ny] == '#') {
break;
}
x = nx;
y = ny;
dist++;
if (map[x][y] == 'O') {
return new MoveResult(x, y, dist, true);
}
}
return new MoveResult(x, y, dist, false);
}
static class State {
int rx, ry, bx, by, depth;
State(int rx, int ry, int bx, int by, int depth) {
this.rx = rx;
this.ry = ry;
this.bx = bx;
this.by = by;
this.depth = depth;
}
}
static class MoveResult {
int x;
int y;
int dist;
boolean inHole;
MoveResult(int x, int y, int dist, boolean inHole) {
this.x = x;
this.y = y;
this.dist = dist;
this.inHole = inHole;
}
}
}'Algorithm' 카테고리의 다른 글
| [백준] 1504. 특정한 최단 경로 (0) | 2025.09.25 |
|---|---|
| [백준] 1753. 최단경로 - JAVA (0) | 2025.09.23 |
| [백준] 14502. 연구소 - JAVA (0) | 2025.09.16 |
| [백준] 3190. 뱀 -JAVA (0) | 2025.09.14 |
| [백준] 2178. 미로탐색 - JAVA (0) | 2025.09.10 |