BOJ 14451

안대 낀 스피드러너

문제출처

문제 해석

  • NxN격자의 맨 왼쪽아래에서 맨 오른쪽 위까지 가는 최소의 경로를 구하는 문제입니다.
  • 처음에 서있는 방향은 위 또는 오른쪽입니다.
  • 현재 칸에서 할 수 있는 행동은 “전진”,”좌회전”,”우회전” 중 하나를 할 수 있습니다.
  • 각 행동을 할 때마다 1초가 걸립니다.
  • 벽 또는 장애물이 있는 칸으로는 이동할 수 없습니다.

설계(10)

  • 해당 문제는 처음 방향이 위, 오른쪽인 두 사람이 행동을 같이 하면서 오른쪽 위로 이동했을 때의 최소 칸수를 구하는 문제입니다.
  • 두 방향을 기준으로 이동한 좌표와 방향정보에 대한 방문 여부를 체크하도록 visited[x1][y1][x2][y2][4][4]를 선업합니다.
  • 총 O(N^4x4^2)의 시간복잡도로 시간 내에 풀이가 가능합니다.

구현(60)

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

int dist[MAXN][MAXN][MAXN][MAXN][4][4];
char map[MAXN][MAXN];
int N;
struct node{
    int x1;
    int y1;
    int x2;
    int y2;
    int dir1;
    int dir2;
};
queue<node> q;
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
int dirs[3] = {-1,0,1};
int bfs(){
    q.push({N-1,0,N-1,0,3,0});
    dist[N-1][0][N-1][0][3][0] = 1;
    int time = 0;
    while (!q.empty()){
        int q_size=  q.size();
        int x1 = q.front().x1;
        int y1 = q.front().y1;
        int x2 = q.front().x2;
        int y2 = q.front().y2;
        int dir1 = q.front().dir1;
        int dir2 = q.front().dir2;
        if (x1 == 0 && x2 == 0 && y1 == N-1 && y2 == N-1) {
            return dist[x1][y1][x2][y2][dir1][dir2]-1;
        }
        q.pop();
        for (int i = 0;i < 3; i++){
            int ndir1 = dir1;
            int ndir2 = dir2;
            int nx1 = x1;
            int ny1 = y1;
            int nx2 = x2;
            int ny2 = y2;
            if (!(x1 == 0 && y1 == N-1)){
                ndir1 = (4 + dir1 + dirs[i])%4;
                if (ndir1 == dir1){
                    nx1 += dx[dir1];
                    ny1 += dy[dir1];
                    if (nx1 < 0 || ny1 < 0 || nx1 >= N || ny1 >=N || map[nx1][ny1] == 'H'){
                        nx1 -= dx[dir1];
                        ny1 -= dy[dir1];
                    }
                }
            }
            if (!(x2 == 0 && y2 == N-1)){
                ndir2 = (4+ dir2 + dirs[i])%4;
                if (ndir2 == dir2){
                    nx2 += dx[dir2];
                    ny2 += dy[dir2];
                    if (nx2 < 0 || ny2 < 0 || nx2 >= N || ny2 >=N || map[nx2][ny2] == 'H'){
                        nx2 -= dx[dir2];
                        ny2 -= dy[dir2];
                    }
                }
            }
            if (dist[nx1][ny1][nx2][ny2][ndir1][ndir2]) continue;
            dist[nx1][ny1][nx2][ny2][ndir1][ndir2] = dist[x1][y1][x2][y2][dir1][dir2]+1;
            q.push({nx1,ny1,nx2,ny2,ndir1,ndir2});
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> N;
    for (int i = 0 ;i  <N;i++){
        for (int j =0;j<N;j++){
            cin >> map[i][j];
        }
    }
    cout << bfs();
    return 0;
}
디버깅
  • 구현이 빡센 문제로, 생각할 부분이 많았습니다.
  • 만약 두 방향중에 하나가 도착지에 도착한 경우에는 이후로 다른 행동을 취하지 않도록 해야하는 점을 간과했습니다.
  • 두 방향의 경로를 독립적으로 생각해서 이중포문으로 각 3행동에 대해 9가지의 경우를 만들어 탐색을 진행하는 실수를 하였습니다. 경로가 같아야 하므로 3행동에 대해서만 확인하면 되었습니다.
제출결과
  • 36ms

마무리

  • bfs탐색의 응용문제였습니다. 무려 6차원 배열이 필요한 만큼 구현이 빡센 느낌의 문제였습니다. 잔실수가 많아 디버깅에 시간을 많이 썼습니다.