BOJ 11451

팩맨

문제출처

문제 해석

  • 두개의 팩맨이 합쳐지기 위한 가장 빠른 시간과 경로를 출력하는 문제입니다.
  • 미로에서 이동하지 않는 유령들과 마주치면 안됩니다.
  • 이동방향을 정하면, 두 팩맨은 정해진 이동방향으로 동시에 이동하게 됩니다.
  • 만약 이동할 칸에 벽이 있으면 이동하지 않습니다.
  • 팩맨이 정해진 범위를 벗어나면 반대편에서 나타나게 합니다.

설계(15)

  • 팩맨이 합쳐지기 위해 필요한 최소시간을 구해야 하기 때문에 bfs를 사용합니다.
  • 두개의 팩맨의 위치를 모두 고려해야 하기 때문에 시간복잡도가 O(NM2)이고 시간안에 충분히 해결이 가능합니다.
  • 팩맨들을 이동할 때 범위가 벗어나는 지 체크한 후 반대편으로 이동시킵니다.
  • 이동할 칸에 유령이 없고 벽이 있으면 이동하지 않게 합니다.
  • 위 문제에서 최소값 뿐만 아니라 경로 또한 출력을 해야하기 때문에 string 변수를 이용해서 경로를 저장해주도록 하였습니다.

구현(20)

코드
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
#define MAXN 50

char map[MAXN][MAXN];
bool visited[MAXN][MAXN][MAXN][MAXN];

struct node {
	int x1;
	int y1;
	int x2;
	int y2;
	string s;
};
node start;
queue<node> q;
int N, M;
string dir = "EWSN";
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
bool is_range(int x, int y) {
	return (x >= 0 && y >= 0 && x < N && y < M);
}
pair<int,int> change_dir(int x, int y, int d) {
	if (d == 0) return { x,0 };
	else if (d == 1) return { x, M - 1 };
	else if (d == 2) return { 0,y };
	return { N - 1,y };
}
string bfs() {
	
	q.push(start);
	visited[start.x1][start.y1][start.x2][start.y2] = true;
	while (!q.empty()) {
		int q_size = q.size();
		while (q_size--) {
			int x1 = q.front().x1;
			int y1 = q.front().y1;
			int x2 = q.front().x2;
			int y2 = q.front().y2;
			string s = q.front().s;
			if (x1 == x2 && y1 == y2) return s;
			q.pop();
			for (int k = 0; k < 4; k++) {
				int nx1 = x1 + dx[k];
				int ny1 = y1 + dy[k];
				int nx2 = x2 + dx[k];
				int ny2 = y2 + dy[k];
				if (!is_range(nx1, ny1)) {
					pair<int, int> tmp = change_dir(nx1, ny1, k);
					nx1 = tmp.first;
					ny1 = tmp.second;
				}
				if (!is_range(nx2, ny2)) {
					pair<int, int> tmp = change_dir(nx2, ny2, k);
					nx2 = tmp.first;
					ny2 = tmp.second;
				}
				if (map[nx1][ny1] == 'G' || map[nx2][ny2] == 'G') continue;
				if (map[nx1][ny1] == 'X') {
					nx1 = x1;
					ny1 = y1;
				}
				if (map[nx2][ny2] == 'X') {
					nx2 = x2;
					ny2 = y2;
				}
				if (visited[nx1][ny1][nx2][ny2]) continue;
				visited[nx1][ny1][nx2][ny2] = true;
				q.push({ nx1,ny1,nx2,ny2,s + dir[k] });
			}
		}
	}
	return "-1";
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
	while (t--) {
		while (!q.empty()) q.pop();
		start = { -1,-1,-1,-1,"" };
		memset(visited, false, sizeof(visited));
		cin >> N >> M;
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < M; j++) {
				cin >> map[i][j];
				if (map[i][j] == 'P') {
					if (start.x1 == -1) start.x1 = i, start.y1 = j;
					else start.x2 = i, start.y2 = j;
					map[i][j] = '.';
				}
			}
		}
		string ans = bfs();
		if (ans == "-1") cout << "IMPOSSIBLE\n";
		else cout << ans.length() << " " << ans << "\n";
	}
	
	return 0;
}
디버깅
제출결과
  • 68ms

마무리

  • 두개의 좌표정보를 사용해야하는 bfs문제입니다. 개인적으로 백준에 있는 구슬탈출과 유형이 비슷해 보입니다.