layered

[백준/C++] 13460 구슬 탈출 2 본문

알고리즘

[백준/C++] 13460 구슬 탈출 2

스윗푸들 2023. 4. 16. 00:04

https://www.acmicpc.net/problem/13460

 

13460번: 구슬 탈출 2

첫 번째 줄에는 보드의 세로, 가로 크기를 의미하는 두 정수 N, M (3 ≤ N, M ≤ 10)이 주어진다. 다음 N개의 줄에 보드의 모양을 나타내는 길이 M의 문자열이 주어진다. 이 문자열은 '.', '#', 'O', 'R', 'B'

www.acmicpc.net

 

그냥 시뮬레이션 해서 구현하는 문제.

공이 네 방향으로 굴러갈 수 있으므로 bfs 기반이지만 공이 굴러가다가 알아서 방향을 트는 건 불가능하니까 약간의 처리를 해 주어야 한다.

 

처음 구상은 이랬다.

 

1 먼저 공이 굴러갈 방향을 찾는다(벽만 없다면 굴러갈 수 있다)

2 공을 가상으로 굴린다

공은 한 번 굴리면 그 방향으로만 쭉 가야 하니까, 차라리 목적지만 딱 찾아서 순간이동 하는 게 이득이라고 생각했다가 나중에 큰 오류를 낳았다

3 방문 처리를 한다

 

그러다가 여러 가지 이유로 틀렸는데,

 

1 빨간 구슬의 상태만 체크하면 안 된다

이 보드는 두 구슬이 상호작용을 하는 형태이므로, 빨간 구슬의 위치가 같아도 파란 구슬의 위치가 다르다면 다른 결과를 불러올 수 있다. 즉, 이 문제는 방문한다기보다는 두 구슬이 이루는 상태를 바라보는 관점이라고 하는 것이 더 적절한 것 같다.

 

2 굴러가는 도중에 구멍이 있을 수도 있다

지금 보면 되게 황당한 실수인데 구슬이 굴러간 곳에만 구멍이 있을 거라고 생각해서 굴러가다가 떨어질 거라는 생각을 못했다. 아마 저 순간이동이라는 단어에 꽂혀서 그런 게 아닐까 싶다(...)

3 굴러갈 수 있는지를 체크하는 건 두 구슬 모두 해당된다

사실 처음에는 빨간 구슬이 굴러갈 수 있을 때에만 구슬을 굴리도록 했다. 구슬이 굴러갈 수 없는 경우는 사방이 벽으로 둘러싸일 때밖에 없을 거라고 생각해서였다. 그런데 이렇게 빨간 구슬의 세 면이 모두 벽이고 옆에는 파란 구슬이 있을 때에는, 파란 구슬을 굴리고 나면 빨간 구슬도 굴릴 수 있게 된다.

 

XXXX

XRB.

XXXX

 

결국 파란 구슬이 굴러갈 수 있는지의 여부도 체크하는 것이 맞다. 1과 같이 두 구슬이 상호작용 하면서 보드의 상태를 만든다는 점에서, 빨간 구슬만 체크하는 건 어느 한 구슬의 움직임을 제한하는 셈이 되어 버리는 것 같기도 하다.

 

코드 구현 과정

1 기본적으로는 bfs의 맥락으로, 경로를 탐색하고 약간의 판별과정을 거쳐 큐에 삽입하는 흐름이다

2 다음 경로로 갈 수 있으면 일단 구슬을 가상으로 굴려 목적지를 찾는다

3 구르다가 중간에 떨어지는 건 blue_fall, red_fall 변수로 처리했다

4 두 구슬이 구르다가 만날 경우 선후관계에 따라 목적지를 조정해 주어야 하므로 굴리면서 카운트를 했다

5 목적지에 잘 도달했다면 보드의 상태를 저장한다

 

* 종료 조건

1 빨간 구슬만 떨어지는 경우 -> 별도로 처리함

2 움직일 수 없는 경우, 10번을 초과한 경우 -> 반복문의 실행 조건과 같음

 

뭐랄까, 내가 쓰긴 했지만 내 스타일의 코드는 아니다...ㅎㅎ

 

#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

struct Node {
	pair<int, int> red;
	pair<int, int> blue;
	int count;
};

void input();
void bfs();

char board[10][10];
int px[4] = { -1, 0, 1, 0 };
int py[4] = { 0, -1, 0, 1 };
bool visited[10][10][10][10];
int n, m;
int ans = -1;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	input();
	bfs();
	cout << ans;
	return 0;
}

void input() {
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			cin >> board[i][j];
}

void bfs() {
	int rx, ry, bx, by;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (board[i][j] == 'R') {
				rx = i;
				ry = j;
			}
			else if (board[i][j] == 'B') {
				bx = i;
				by = j;
			}
		}
	}
	
	queue<Node> queue;
	queue.push({ { rx, ry }, { bx, by }, 0 });
	visited[rx][ry][bx][by] = true;

	while (!queue.empty() && queue.front().count < 10) {
		rx = queue.front().red.first;
		ry = queue.front().red.second;
		bx = queue.front().blue.first;
		by = queue.front().blue.second;
		int count = queue.front().count;

		for (int i = 0; i < 4; i++) {
			int nrx = rx + px[i];
			int nry = ry + py[i];
			int nbx = bx + px[i];
			int nby = by + py[i];
			
			if (board[nrx][nry] != '#' || board[nbx][nby] != '#') {// 굴릴 수 있나?
				bool red_fall = false;
				bool blue_fall = false;
				bool next = false;

				int rc = 0;
				int bc = 0;
				while (board[nbx][nby] != '#') { // 파란 구슬 굴림
					if (nbx == rx && nby == ry)
						next = true;
					if (board[nbx][nby] == 'O')
						blue_fall = true;
					nbx += px[i];
					nby += py[i];
					bc++;
				}
				nbx -= px[i];
				nby -= py[i];

				while (board[nrx][nry] != '#') { // 빨간 구슬 굴림
					if (nrx == bx && nry == by)
						next = true;
					if (board[nrx][nry] == 'O')
						red_fall = true;
					nrx += px[i];
					nry += py[i];
					rc++;
				}
				nrx -= px[i];
				nry -= py[i];



				if (next) { // 굴러가다가 만나는 경우에 대한 위치 조정
					if (rc > bc) {
						nrx -= px[i];
						nry -= py[i];
					}
					else {
						nbx -= px[i];
						nby -= py[i];
					}

				}


				if (!visited[nrx][nry][nbx][nby] && !blue_fall && !red_fall) { // 둘 다 안 떨어짐
					visited[nrx][nry][nbx][nby] = true;
					queue.push({ { nrx, nry }, { nbx, nby }, count + 1 });
				}

				if (!blue_fall && red_fall) { // 빨간 구슬만 떨어짐
					ans = count + 1;
					return;
				}
			}
		}
		queue.pop();
	}
}

 

'알고리즘' 카테고리의 다른 글

[백준/C++] 27893 특별한 드롭킥  (1) 2023.04.25
[백준/C++] 13334 철로  (1) 2023.04.17
[백준/C++] 27447 주문은 토기입니까?  (0) 2023.04.14
[백준/C++] 27740 시프트 연산  (2) 2023.04.12