문제

백준 13460 구슬 탈출 2 문제 보러가기



풀이

처음 접근 방법은 DFS였으나 방문처리 과정에서 코드가 꼬여 풀이법을 참고한 결과 BFS로 쉽게 풀릴 수 있는 문제임을 알았다. 처음엔 빨간 구슬의 방문처리만 해줬는데 이동하는 한칸한칸 모두 해주다보니 원하는 결과가 나오지 않았다. 방문처리는 구슬을 굴렸을 때 벽에 부딪히거나 ‘O’에 들어갔을 때의 그 위치만 해주면 되는데 한칸한칸 방문처리를 해주다 보니 이상한곳에 시간을 낭비했다 ㅠㅠ…

또한 4방향 탐색에서 벽(#)인 부분이 있으면 애초에 갈 생각도 안했는데 반례를 찾다보니 빨간 구슬의 4방향을 모두 탐색해야지만 풀리는 테스트케이스가 있음을 깨달았다.(예를 들어 파란구슬을 가두어 둔 뒤 빨간구슬을 움직이는 경우)

그리고 queue에 구슬의 위치를 저장할 때, 난 방문처리를 미리 해준 뒤에 저장했는데 구슬을 먼저 굴려보고 구슬의 현재 위치가 이미 갔던 곳이지 확인을 한 뒤 처음 방문일때만 queue에 저장을 해줘야했다.

문제에 나와있는 모든 조건을 코드에 담으려 하지 않아도 문제는 풀렸다. 파란구슬이 ‘O’에 빠지면 무조건 -1을 출력한다는 편견과 두 구슬이 위치가 같을 수 있고 그럴 경우엔 계산을 해서 위치를 바꿔주면 되는데 빨간구슬 먼저 움직이고 파란구슬 움직이고… 순서를 정할 필요가 없었다 ㅠㅠ

첫 통과때의 메모리는 177024 KB에 시간은 628 ms가 걸렸는데 공개가 되어있는 다른 사람들의 풀이를 참고해 차근차근 다시 풀었고, 14916 KB, 140 ms 라는 최적값을 낼 수 있었다.



풀이

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

public class Main {
	static Queue<Point> queue = new LinkedList<>();
	static char[][] map;
	static boolean visited[][][][];
	static int N, M;
	static int[][] dir = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };

	static class Point {
		int rx, ry, bx, by, count;
	}

	static Point startPoint = new Point();

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		N = Integer.parseInt(st.nextToken());
		M = Integer.parseInt(st.nextToken());

		map = new char[N][M];
		visited = new boolean[N][M][N][M];

		for (int i = 0; i < N; i++) {
			String tmp = br.readLine();
			for (int j = 0; j < M; j++) {
				map[i][j] = tmp.charAt(j);
				if (map[i][j] == 'R') {
					startPoint.rx = i;
					startPoint.ry = j;
				}

				if (map[i][j] == 'B') {
					startPoint.bx = i;
					startPoint.by = j;
				}
			}
		}

		startPoint.count = 0;
		System.out.println(bfs());
	}

	public static int bfs() {
		int result = -1;

		/* 1. 큐에 시작점 넣고 방문처리 해주기 */
		queue.add(startPoint);
		visited[startPoint.rx][startPoint.ry][startPoint.bx][startPoint.by] = true;

		/* 2. bfs 돌리기 */
		while (!queue.isEmpty()) {
			Point now = queue.poll();

			if (now.count > 10)
				break; // 10번 이하로만 굴리기

			if (map[now.rx][now.ry] == 'O' && map[now.bx][now.by] != 'O') {
				result = now.count;
				break;
			}

			/* 4방향 탐색 */
			for (int d = 0; d < 4; d++) {
				int nrx = now.rx;
				int nry = now.ry;
				int nbx = now.bx;
				int nby = now.by;

				// 공 굴리기
				int[] red = move(nrx, nry, d);
				nrx = red[0];
				nry = red[1];
				int[] blue = move(nbx, nby, d);
				nbx = blue[0];
				nby = blue[1];

				// 빨간공과 파란공 위치가 같으면 위치를 수정해준다.
				if (nrx == nbx && nry == nby) {
					if (map[nrx][nry] != 'O') {
						int rdist = Math.abs(nrx - now.rx) + Math.abs(nry - now.ry);
						int bdist = Math.abs(nbx - now.bx) + Math.abs(nby - now.by);

						if (rdist > bdist) { // 빨간공이 파란공보다 뒤에 있다.
							nrx -= dir[d][0];
							nry -= dir[d][1];
						} else {
							nbx -= dir[d][0];
							nby -= dir[d][1];
						}
					}
				}

				/* 4. 방문 안한 Point를 큐에 넣고 방문처리 해준다. */
				if (!visited[nrx][nry][nbx][nby]) {
					Point newPoint = new Point();
					newPoint.rx = nrx;
					newPoint.ry = nry;
					newPoint.bx = nbx;
					newPoint.by = nby;
					newPoint.count = now.count + 1;

					queue.add(newPoint);
					visited[nrx][nry][nbx][nby] = true;
				}
			}
		}

		return result;
	}

	public static int[] move(int x, int y, int d) {
		while (true) {
			if (map[x][y] != '#' && map[x][y] != 'O') {
				x += dir[d][0];
				y += dir[d][1];
			} else {
				if (map[x][y] == '#') {// 'O'일 수 있으니까
					x -= dir[d][0];
					y -= dir[d][1];
				}

				break;
			}
		}

		return new int[] { x, y };
	}
}



반례

4 5
#####
#.BR#
#.O##
#####

answer : 2

10 9
#########
#.##....#
#.#.#####
#####...#
#.####R##
####....#
###BO####
#.#.###.#
###...#.#
#########

answer : 3

7 6
######
##..B#
#..#.#
#R...#
##..O#
#....#
######

answer : 4

5 5
#####
#.#.#
#####
#BOR#
#####

answer : 1

6 6
######
##...#
#...##
#B.###
#OR#.#
######

answer : 1

8 6
######
#.BR.#
##O.##
#....#
##...#
#..###
#....#
######

answer : 2

9 7
#######
##.##.#
####.##
###O###
#..BR.#
###.#.#
#.#...#
##.#..#
#######

answer : 5

8 5
#####
###.#
#.O##
##BR#
##.##
#..##
#.###
#####

answer : 3

5 5
#####
#.#R#
###.#
#.BO#
#####

answer : 1

8 8
########
#.#.####
######.#
#..#.###
#BO..R.#
#.#...##
##.###.#
########

answer : 1

9 6
######
#.B#.#
#..R##
##.#.#
#..#.#
#O.#.#
#..#.#
#....#
######

answer : 9

6 7
#######
#.#.R##
#B..#.#
#.....#
#O#..##
#######

answer : 6

10 6
######
#BO.##
#...##
###.##
##...#
###..#
###R##
#.#..#
##.#.#
######

answer : 2

5 9
#########
#.##....#
#..R..###
#O#B##..#
#########

answer : 2

8 7
#######
##.####
#.##.B#
##...R#
###..O#
##..#.#
#####.#
#######

answer : 6

9 9
#########
##.R..###
##..#####
###.#.#.#
##.B#.#.#
#.....###
#.O..#.##
###..####
#########

answer : 2

7 5
#####
###O#
#...#
##.##
##..#
#.BR#
#####

answer : 4

6 10
##########
##O..#..##
#.#.##.###
#.#..B..R#
#######.##
##########

answer : 5

8 5
#####
#.###
#...#
#...#
#O.##
#.###
#.RB#
#####

answer : 2