Algorithm

백준_구슬 탈출 2_13460

Young_J 2021. 1. 18. 22:35

//알고리즘

1. bfs 

 

2. 방문배열을 4차원 배열로 가져가야하는 문제

 -> 빨간공과 파란공이 같이 움직이므로 각각의 좌표에 해당하는 방문배열을 만들어야 함.

 

3. 이동 

 -> 보통 빨간색을 먼저 이동시킴

 -> 빨간색의 1칸 이동범위에 파란색이 있을 경우 파란색부터 움직여야 함. (엣지 케이스일듯?)

 

4. 결과

 -> 공이 들어갔을 경우를 확인하기위해 boolean R, B 사용

 -> boolean b,r 은 벽면에 닿았을 때를 의미함. (더이상 갈 수 없을 경우)

 

더보기
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class 구슬탈출2_13460 {

	static int N, M, Hr, Hc;
	static int[] dr = { -1, 1, 0, 0 };
	static int[] dc = { 0, 0, -1, 1 };
	static char map[][];
	static boolean v[][][][];

	static class Point {
		int Rr, Rc, Br, Bc, cnt;

		public Point(int rr, int rc, int br, int bc, int cnt) {
			super();
			Rr = rr;
			Rc = rc;
			Br = br;
			Bc = bc;
			this.cnt = cnt;
		}

	}

	public static void main(String[] args) throws Exception {
		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][];
		v = new boolean[N][M][N][M];

		for (int i = 0; i < N; i++) {
			map[i] = br.readLine().toCharArray();
		}

		int Rr = 0, Rc = 0, Br = 0, Bc = 0;
		for (int i = 0; i < map.length; i++) {
			for (int j = 0; j < map[i].length; j++) {
				if (map[i][j] == 'R') {
					Rr = i;
					Rc = j;
				} else if (map[i][j] == 'B') {
					Br = i;
					Bc = j;
				} else if (map[i][j] == 'O') {
					Hr = i;
					Hc = j;
				}
			}
		}

		System.out.println(bfs(Rr, Rc, Br, Bc));

	}

	private static int bfs(int rr, int rc, int br, int bc) {
		Queue<Point> q = new LinkedList<>();
		q.add(new Point(rr, rc, br, bc, 1));

		Point p = null;
		while (!q.isEmpty()) {
			p = q.poll();

			if (v[p.Rr][p.Rc][p.Br][p.Bc])
				continue;
			v[p.Rr][p.Rc][p.Br][p.Bc] = true;
			if (p.cnt == 11)
				continue;

			for (int k = 0; k < 4; k++) {

				if (p.Rr + dr[k] == p.Br && p.Rc + dc[k] == p.Bc) {
					int Rnr = p.Rr;
					int Rnc = p.Rc;
					int Bnr = p.Br;
					int Bnc = p.Bc;
					boolean B = false;
					boolean R = false;
					boolean b = true;
					boolean r = true;
					while (b || r) {
						Rnr += dr[k];
						Rnc += dc[k];
						Bnr += dr[k];
						Bnc += dc[k];

						if (map[Bnr][Bnc] == 'O') {
							B = true;
						}

						if (map[Bnr][Bnc] == '#' || (Bnr == Rnr - dr[k] && Bnc == Rnc - dr[k])) {
							Bnr -= dr[k];
							Bnc -= dc[k];
							b = false;
						}

						if (map[Rnr][Rnc] == 'O') {
							R = true;
						}

						if (map[Rnr][Rnc] == '#' || (Rnr == Bnr && Rnc == Bnc)) {
							Rnr -= dr[k];
							Rnc -= dc[k];
							r = false;
						}

					}
					if (R && !B) {
						return p.cnt;
					}
					if (!R && !B) {
						q.add(new Point(Rnr, Rnc, Bnr, Bnc, p.cnt + 1));
					}
				} else {
					// 아닐 떄
					int Rnr = p.Rr;
					int Rnc = p.Rc;
					int Bnr = p.Br;
					int Bnc = p.Bc;
					boolean B = false;
					boolean R = false;
					boolean b = true;
					boolean r = true;
					while (b || r) {
						Rnr += dr[k];
						Rnc += dc[k];
						Bnr += dr[k];
						Bnc += dc[k];

						if (map[Rnr][Rnc] == 'O') {
							R = true;
						}

						if (map[Rnr][Rnc] == '#' || (Rnr == Bnr - dr[k] && Rnc == Bnc - dc[k])) {
							Rnr -= dr[k];
							Rnc -= dc[k];
							r = false;
						}

						if (map[Bnr][Bnc] == 'O') {
							B = true;
						}

						if (map[Bnr][Bnc] == '#' || (Bnr == Rnr && Bnc == Rnc)) {
							Bnr -= dr[k];
							Bnc -= dc[k];
							b = false;
						}

					}
					if (R && !B) {
						return p.cnt;
					}
					if (!R && !B) {
						q.add(new Point(Rnr, Rnc, Bnr, Bnc, p.cnt + 1));
					}

				}

			}

		}

		return -1;

	}

}

 

※ bfs, dfs탐색문제는 나름 자신있었는데 이 문제는 생각보다 많이 어려웠던 문제,  엣지케이스도 눈에 잘 안보이고 4차원 방문배열로 접근한다는 자체를 떠올리기가 어려움. 매 이동마다 새로운배열을 생성하여 가져갈려고 했지만 그렇게 구현했을 경우 메모리 초과가 났을거같음. 지금까지 풀어본 bfs, dfs 문제중에 난이도가 매우 높았던 문제인듯.