[13460] 구슬 탈출 2

3 minute read

풀이

​최적화된 풀이인지는 모르겠지만 다음과 같은 알고리즘으로 풀면 정답을 받을 수 있습니다.

  1. ​R의 좌표를 rx, ry B의 좌표를 bx, by라고 할 때, queue<tuple<rx,ry,bx,by> > q;를 사용해서 BFS를 전개합니다. 이는 R와 B의 좌표를 동시에 다뤄야 함을 의미합니다. ​2. 위에서 만든 Queue에는 R와 B의 시작 좌표 그리고 이 좌표에서부터 특정 방향으로 기울였을 때 R와 B가 멈추는 위치가 다시 Push됩니다. ​3. 최단 경로를 찾아야 하므로 ​ while(!q.empty()) -> int qs = q.size() -> qs번 for문 실행 -> 그 안에서 dx dy 4 방향으로 점검을 해야합니다.( 이 부분이 무슨 말인지 모르겠으면 아래 코드를 보면 됩니다.)
  2. 2번에서 Push를 현재의 위치에서 특정 방향으로 움직이는 것은 더이상 움직이지 못하는 경우가 될 때까지 계속 움직입니다. 더이상 움직이지 못하는 경우는 두 가지 입니다. 벽을 만나거나 구멍에 빠졌을 때 입니다.
  3. R와 B를 움직일 때는 R와 B를 겹치는 것은 나중에 판단하고 R와 B를 각각 벽을 만나지도 않고, 구멍에 빠지지도 않는 경우인지 판단하고 멈출 때까지 이동시킵니다.
  4. 만약 R와 B가 멈춘 위치가 같다면 R와 B중 욺직인 거리가 더 큰 것을 뒤로 한 칸 뺴서 겹치지 않도록 해줍니다. #00R00B00# -(오른쪽으로 기울이기)-> #000000RB#
  5. 이렇게 최종적으로 R과 B가 멈춘 위치를 방문했다고 표시합니다.
  6. R만 O에 도착했고, B는 아니라면 정답, 둘 다 O에 도착했다면 종료. 둘 다 O가 아니라면 q에 push합니다.​
  7. 3번을 진행하다가 10번째까지 움직인 경우 프로그램을 종료합니다.

특징

  1. map이 2차원일 때 check 배열을 2차원 이상으로 구성해서 풀 수도 있다.
  2. na,nb,nc,nd를 생성할 때 어차피 #를 만나면 멈출 것이므로, 맵의 가장자리가 모두 #로 주어질 때는 인덱스 초과 구문을 짜지 않아도 된다.
  3. 입력값이 주어지는 모습을 보고 R,B,O가 각각 주어져야하므로 주어지는 값부터 R,B,O가 겹치는 경우는 고려하지 않아도 된다.
  4. 풀이를 보고 이해 다 하고, 그대로 짜는 것만 1시간이 걸렸고 3번의 실패를 틀렸습니다. 를 받았다.

코드


#include <cstdio>
#include <queue>
#include <tuple>
#include <algorithm>
#define MAX 10
using namespace std;
char map[MAX + 1][MAX + 1];
int n = 0, m = 0;
queue<tuple<int, int, int, int> > q;
//우, 좌, 하, 상
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
bool check[MAX + 1][MAX + 1][MAX + 1][MAX + 1];

int bfs(int a, int b, int c, int d) {
	int ca = 0, cb = 0, cc = 0, cd = 0; //curr
	int na = 0, nb = 0, nc = 0, nd = 0; //next
	int ans = 1;
	q.push(make_tuple(a, b, c, d));
	check[a][b][c][d] = true;
	while (!q.empty()) {
		int qs = q.size();
		for (int s = 0; s < qs; s++) {
			tie(ca, cb, cc, cd) = q.front(); q.pop();
			for (int k = 0; k < 4; k++) {
				tie(na, nb, nc, nd) = tie(ca, cb, cc, cd);
				//벽을 만나거나 구멍에 빠지지 않는다면 k방향으로 끝까지 움직이기
				while (map[na + dx[k]][nb + dy[k]] != '#' && map[na][nb] != 'O') {
					na += dx[k]; nb += dy[k];
				}
				while (map[nc+dx[k]][nd+dy[k]] != '#' && map[nc][nd] != 'O') {
					nc += dx[k]; nd += dy[k];
				}
				//printf("NEXT %d, %d, %d, %d\n", na, nb, nc, nd);
				if (na == nc && nb == nd) {
					//둘 다 구멍에 빠진 경우
					if (map[na][nb] == 'O') continue;
					//겹치면 더 많이 움직인 것을 뒤로 한 칸 빼주기
					if (abs(na - ca) + abs(nb - cb) > abs(nc - cc) + abs(nd - cd)) {
						na -= dx[k]; nb -= dy[k];
					}
					else {
						nc -= dx[k]; nd -= dy[k];
					}
				}
				//B가 구멍에 빠진 경우는 제외
				if (map[nc][nd] == 'O') continue;
				//R만 구멍에 빠진 경우는 정답
				if (map[na][nb] == 'O') return ans;
				//둘 다 구멍에 빠지지 않았으면
				if (check[na][nb][nc][nd] == false) {
					check[na][nb][nc][nd] = true;
					q.push(make_tuple(na, nb, nc, nd));
				}
			}
		}
		if (ans == 10) {
			return -1;
		}
		ans += 1;
	}
	return -1;
}

int main(void) {
	int a = 0, b = 0, c = 0, d = 0; //a,b = R, c,d = B
	scanf("%d%d", &n, &m); getchar();
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			scanf("%c", &map[i][j]);
			if (map[i][j] == 'R') {
				a = i; b = j;
			}
			else if (map[i][j] == 'B') {
				c = i; d = j;
			}
		}
		getchar();
	}
	printf("%d\n", bfs(a,b,c,d));
	return 0;
}

Tags:

Categories:

Updated: