BOJ 18224

미로에 갇힌 건우

문제출처

문제 해석

  • NxN미로에서 가장 왼쪽 위-> 가장 오른쪽 아래로 가기 위한 최단 시간(몇번째 날)을 구하는 문제입니다.
  • 낮과 밤 상태가 M번 이동할 때마다 바뀝니다(초기상태는 낮).
  • 밤에는 가능한 경우 연속된 벽을 뛰어넘을 수 있습니다.(착지할 곳이 범위를 벗어나는 경우 뛰어넘을 수 없습니다)

설계(20)

  • 낮과 밤 상태를 구분해야 하는 까다로운 문제입니다.
  • 정답률이 아주 낮아 곰곰히 오래 생각을 했는데, 여기서 키포인트는 한번 지나간 곳을 더 지나가서 밤에 벽을 한번에 뛰어넘는 경우를 고려해야 한다는 것입니다.
  • 이를 구현하기 위해서 visited[x][y][밤/낮 상태][밤/낮 동안 이동거리]를 이용합니다.
  • 총 시간복잡도는 O(NxNx2xM) = O(N^2xM)정도로 시간안에 해결이 가능합니다.
  • 구현이 어려웠던 부분중 하나는 큐에 어떠한 정보를 넣느냐였습니다. 고려해야하는 부분은 이동한 칸수,현재 낮/밤 상태를 표현하는 정보였습니다.
  • (현재 칸수+1)/M을 올림한 값의 2의 나머지로 밤/낮 상태를 표현할 수 있습니다.
  • 현재의 낮/밤 상태에 따라 벽을 이동할 지의 유무를 판단하도록 하였습니다.

구현(60)

코드
#include <iostream>
#include <queue>
#include <cstring>
#include <math.h>
using namespace std;
#define MAXN 501

struct node{
    int x;
    int y;
    int flag;
    int cnt;
};
bool visited[MAXN][MAXN][2][11];
int map[MAXN][MAXN];
int N,M;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
queue<node> q;
bool is_range(int x, int y){
    return (x>=0 && y >= 0 && x <N && y <N);
}
pair<int,int> bfs(){

    q.push({0,0,0,1});
    visited[0][0][0][1] = true;
    while (!q.empty()){
        int x = q.front().x;
        int y = q.front().y;
        int cnt = q.front().cnt;
        int flag = q.front().flag;
        q.pop();
        if (x == N-1 && y == N-1){
            return {flag,(int)ceil((double)cnt/(M*2))};
        }
        for (int k = 0; k< 4; k++){
            int nx = x + dx[k];
            int ny = y + dy[k];
            if (!is_range(nx,ny)) continue;
            int next_state = (int)ceil((double)(cnt+1)/M) % 2==1 ? 0 : 1;
            int next_cnt = (cnt+1)%M;
            if (visited[nx][ny][next_state][next_cnt]) continue;
            // 현재 상태가 낮일 경우
            if (!flag){
                if (map[nx][ny]) continue;
                visited[nx][ny][next_state][next_cnt] = true;
                q.push({nx,ny,next_state,cnt+1});
            }
            // 현재 상태가 밤일 경우
            else{
                if (map[nx][ny]){
                    while (map[nx][ny]){
                        nx += dx[k];
                        ny += dy[k];
                    }
                    if (!is_range(nx,ny) || 
                    visited[nx][ny][next_state][next_cnt])
                    continue;
                    visited[nx][ny][next_state][next_cnt] = true;
                    q.push({nx,ny,next_state,cnt+1});
                }
                else{
                    visited[nx][ny][next_state][next_cnt] = true;
                    q.push({nx,ny,next_state,cnt+1});
                }
            }
        }
    }
    return {-1,-1};
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> N >> M;

    for (int i =0 ; i < N; i++){
        for (int j =0;j<N; j++){
            cin >> map[i][j];
        }
    }
    pair<int,int> ans = bfs();
    if (ans.second == -1) cout <<"-1\n";
    else{
        cout << ans.second <<" ";
        if (ans.first== 0) cout << "sun\n";
        else cout <<"moon\n";
    }
    return 0;
}
디버깅
  • 처음 큐에 넣을 때 이동거리 정보를 0으로 하는지 1로 할 지 헷갈렸습니다. 낮상태부터 시작해야하는 것을 고려하여 1로 시작해야 했습니다.
  • 큐를 함수 내에 선언하여 런타임에러가 나왔습니다. 스택메모리가 overflow되어 안되는 것으로 판단하고, 전역변수로 선언하였습니다.
제출결과
  • 308ms

마무리

  • 자료구조의 형태 선정 및 파싱이 까다로웠던 bfs문제였습니다. 정답률이 낮은만큼 생각을 많이 했고, 반례를 찾아 구현할 수 있었던 문제였습니다.