BOJ 6593 — 상범 빌딩

Dojo
POCS
Published in
7 min readAug 6, 2019

문제

시작 지점에서 도착 지점에 도달할 때까지 걸린 시간을 구하여라.

빌딩은 정육면체 구조로 이루어져 있다. 대각선으로 움직일 수 없으며 현재 있는 칸에 인접한 6개의 칸(동,서,남,북,상,하)으로만 이동이 가능하다.

빨간색은 현재 위치, 초록색은 이동 가능한 위치

한 칸을 이동하는데 걸리는 시간은 1분이다.

입력

세 개의 정수 L(빌딩 층 수), R(빌딩 행 수), C(빌딩 열 수)가 주어진다. 그 다음 각 줄이 C개의 문자로 이루어진 R개의 행이 L번 주어진다. 각 문자는 빌딩의 한 칸을 나타낸다.

문자 ‘S’는 시작 지점을 표현하며 ‘E’는 도착 지점인 출구를 표현한다.

문자 ‘.’은 지나갈 수 있는 칸이며 ‘#’은 막혀있어 지나갈 수 없는 칸이다.

입력의 끝은 L, R, C가 모두 0으로 주어질 때이다.

Ex)

// 첫번째 테스트 케이스
3 4 5
S....
.###.
.##..
###.#

#####
#####
##.##
##...

#####
#####
#.###
####E

// 두번쨰 테스트 케이스
1 3 3
S##
#E#
###

// 입력의 끝
0 0 0

출력

각 테스트 케이스마다 탈출하는 데에 필요한 최단 시간을 아래와 같이 출력한다.

Escaped in X minute(s).

여기서 X 는 최단시간을 의미한다.

탈출이 불가능한 경우 다음과 같이 출력한다.

Trapped!

접근

  • 빌딩 구조를 담을 3차원 배열이 필요
  • 좌표가 3차원 이므로 std::pair<T1, T2>를 이용하지 못해 구조체(struct)를 사용
  • 두 노드 사이의 최단 경로를 찾아야 하므로 DFS가 아닌 BFS를 사용
  • 하나의 칸을 이동할 때마다 시간을 증가시켜 최단 시간을 도출

구현

시작 지점을 저장할 구조체를 만든다.

struct Pos {
int l, r, c;
};

l은 level이며, r은 row, c 는column을 뜻한다.

전역 변수로 입력 받을 빌딩의 층, 행, 열 수를 선언하고. 빌딩을 담을 3차원 배열과 BFS탐색에 필요한 방문 체크 배열도 함께 만들어준다.

int L, R, C;
char map[31][31][31];
bool visited[31][31][31];
int dir[6][3] = {{ 1,0,0 }, { 0,1,0 }, { 0,0,1 }, { -1,0,0 }, {0, -1, 0}, { 0,0,-1 }};

dir배열은 현재 위치에서 인접한 6개의 칸으로 이동하는 각 좌표를 담은 것이다. 예를 들어 현재 좌표가 (0, 0, 0)일 때 dir[0]을 더해주면 다음 좌표는 (1, 0 ,0)이 된다.

main()

main함수 에 전체적인 흐름을 크게 3가지로 나누었다.

  1. L, R, C를 입력받아 모두 0이 아닌 이상 빌딩의 구조를 입력받고 pos에 시작 위치를 저장한다.
  2. BFS탐색을 시작 위치부터 돌려 도착지점까지 최단 경로를 찾고, 최단 경로를 이동하는 데에 걸리는 시간이 최단 시간이므로 이를 반환한다.
  3. 탈출 여부를 따져 그에 걸맞는 출력을 하고 다음 테스트 케이스를 위해 사용했던 map배열과 방문체크 배열 visited를 초기화 한다.
int main(void)
{
// 테스트 케이스의 수가 정해지지 않아 while문으로 계속 반복
while (true) {
Pos pos;
cin >> L >> R >> C;
// 입력의 끝이 주어지면 종료
if (L == 0 && R == 0 && C == 0) {
break;
}
// 빌딩 구조와 시작 지점 저장
input(&pos);

// 최단 시간을 도출
int min = bfs(pos);

if (min > 0) {
cout << "Escaped in " << min << " minute(s)." << '\n';
}
else {
cout << "Trapped!" << '\n';
}
// 초기화
clear();
}
return 0;
}

input()

아주 단순한 코드이며 문자 ‘S’가 입력될 시 시작 위치로 지정한다.

void input(Pos* p)
{
for (int i = 0; i < L; i++) {
for (int j = 0; j < R; j++) {
for (int k = 0; k < C; k++) {
cin >> map[i][j][k];
// 시작 위치 지정
if (map[i][j][k] == 'S') {
p->l = i, p->r = j, p->c = k;
}
}
}
}
}

bfs()

문제 해결의 핵심 부분인 최단 경로를 통한 최단 시간을 도출하는 함수이다. time이 라는 변수를 만들어 하나의 칸을 이동할 때마다 +1을 해주어 전체적인 시간을 나타내게 한다. 예를 들어, 한 칸을 이동하는 데에 걸리는 시간인 1분이 지난다면 각각의 시작 위치에서 갈 수 있는 모든 칸을 queue에 넣고 시간을 늘리는 것이다. 아래 그림은 위 예제가 time = 0일 때이다.

빨간색은 각 시작점을 나타내며 초록색은 이동이 가능한 칸, 파란색은 막혀있는 칸, 노란색은 도착 지점이다.

time = 1일 때,

검정색 칸은 이미 방문한 칸을 나타낸다.

time = 2일 떄,

… 계속 반복하여 …

time = 10일 때,

그러므로 다음 time = 11일 때 도착지점에 도달한 것을 알 수 있다.

int bfs(Pos p)
{
int time = 0;
queue<Pos> q;
visited[p.l][p.r][p.c] = true; // 방문체크
q.push(p); // 시작 위치 S를 queue에 삽입

while (!q.empty()) {
int qSize = q.size();
for (int i = 0;i < qSize;i++) {
int l = q.front().l;
int r = q.front().r;
int c = q.front().c;
q.pop();

//도착 지점에 도달하면 time을 반환
if (map[l][r][c] == 'E') {
return time;
}
if (l >= 0 && l < L && r >= 0 && r < R && c >= 0 && c < C) {
// 각 시작점에 인접한 6개의 방향에 대한 탐색
for (int i = 0; i < 6; i++) {
int n_l = l + dir[i][0];
int n_r = r + dir[i][1];
int n_c = c + dir[i][2];

// 방문하지 않았고 지나갈 수 있다면 queue에 삽입
if (!visited[n_l][n_r][n_c] && map[n_l][n_r][n_c] != '#') {
visited[n_l][n_r][n_c] = true;
p.l = n_l, p.r = n_r,p.c = n_c; q.push(p);
}
}
}
}
time++;
}
return 0; // 탈출이 불가능한 경우 0을 반환
}

clear()

clear함수 또한 아주 간단하며 map배열을 돌면서 모두 지나갈 수 없는 칸으로 초기화하고 방문 여부도 초기화한다.

void clear()
{
for (int i = 0; i <= L; i++) {
for (int j = 0; j <= R; j++) {
for (int k = 0; k <= C; k++) {
map[i][j][k] = '#';
visited[i][j][k] = false;
}
}
}
}

출처

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

--

--