# BOJ14503 로봇 청소기

https://www.acmicpc.net/problem/14503 (opens new window)

로봇 청소기가 주어졌을 때, 청소하는 역역의 개수를 구한다.

로봇 청소기가 있는 장소는 N x M 크기의 직사강형으로 나타낼 수 있으며, 1 x 1 크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 벽(1) 또는 빈 칸(0)이다. 청소기는 바라보는 방향이 있으며, 이 방향은 동, 서, 남, 북 중 하나이다. 지도의 각 칸은 (r, c)로 나타낼 수 있고, r은 북쪽으로 부터 떨어진 칸의 개수, c는 서쪽으로 부터 떨어진 칸의 개수이다.

로봇 청소기는 아래와 같이 작동한다.

  1. 현재 위치를 청소한다.
  2. 현재 위치에서 현재 방향을 기준으로 왼쪽 방향부터 차례대로 인접한 칸을 탐색한다.
    1. 왼쪽 방향에 아직 청소하지 않은 공간이 존재한다면, 그 방향으로 회전한 다음 한 칸을 전진하고 1번부터 진행한다.
    2. 왼쪽 방향에 청소할 공간이 없다면, 그 방향으로 회전하고 2번으로 돌아간다.
    3. 네 방향 모두 청소가 이미 되어있거나 벽인 경우, 바라보는 방향을 유지한 채로 한 칸 후진을 하고 2번으로 돌아간다.
    4. 네 방향 모두 청소가 이미 되어 있거나 벽이면서, 뒤쪽 방향이 벽이라 후진도 할 수 없는 경우 작동을 멈춘다.

로봇 청소기는 이미 청소되어 있는 칸을 또 청소하지 않으며, 벽을 통과할 수 없다.

# 현재 위치를 청소한다.

private static void clean(int row, int col) {
    if (graph[row][col] != 2) {
        graph[row][col] = 2;
        count++;
    }
}

청소를 완료한 곳은 2로 채운다. 또한 청소를 완료한 칸이므로 count를 증가시킨다.

# 왼쪽 방향으로 회전

private static int rotate(int direction) {
    direction--;
    if (direction == -1) {
        direction = 3;
    }
    return direction;
}

direction이 0인 경우 북쪽, 1인 경우 동쪽, 2인 경우 남쪽, 3인 경우 서쪽을 바라보고 있다. 표현하면 아래와 같다.

    0
    북
3       1
서      동
    2
    남

즉 왼쪽으로 회전하면 direction 값이 1씩 줄어든다. 만약 -1이 된 경우 서쪽이므로 조건문을 통해 3으로 변경시킨다.

# 왼쪽 방향부터 차례대로 탐색 진행

private static void operate(int row, int col, int direction) {
    // 1. 현 위치 청소
    clean(row, col);

    // 2. 왼쪽 방향부터 차례대로 탐색 진행
    for (int i = 0; i < 4; i++) {
        direction = rotate(direction);
        int nextRow = row + dr[direction];
        int nextCol = col + dc[direction];

        if (isLocation(nextRow, nextCol) && graph[nextRow][nextCol] == 0) {
            operate(nextRow, nextCol, direction);
            return;
        }
    }

    // 3. 네 방향 모두 청소가 되어 있거나 벽인 경우
    reverse(row, col, direction);
}

반복문을 살펴보면 4가지 방향을 돌아가며 청소가 가능한 경우를 탐색한다. 한 가지 주의해야 할 점은 일반적인 dfs에서 처럼 재귀를 활용하면 안된다. 이전으로 돌아가는 경우는 모두 청소가 되어 있는 경우 뿐이기 때문에 재귀를 진행 한 후 즉시 return으로 더 이상의 로직을 진행하지 않도록 해야 한다.

# 네 방향 모두 청소가 되어 있거나 벽인 경우

만약 반복문에서 반환되지 않고 완료된 경우 네 방향에 갈 수 있는 공간이 없다는 의미이다. 이제 바라보는 방향을 유지한 채 한 칸 후진을 진행해야 한다.

private static void reverse(int row, int col, int direction) {
    int backDirection = (direction + 2) % 4;
    int backRow = row + dr[backDirection];
    int backCol = col + dc[backDirection];
    if (isLocation(backRow, backCol) && graph[backRow][backCol] != 1) {
        operate(backRow, backCol, direction);
    }
}

위 회전 로직과 유사하지만 한 가지 주의해야 할 점은 direction이 유지되도록 operate를 호출해야 한다.

# 제출 코드

아래는 최종 제출 코드이다.

public class BOJ14503 {
    private static int n;
    private static int m;
    private static int count;
    private static int[][] graph;
    public static int[] dr = {-1, 0, 1, 0};
    public static int[] dc = {0, 1, 0, -1};

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        n = scanner.nextInt();
        m = scanner.nextInt();
        int row = scanner.nextInt();
        int col = scanner.nextInt();
        int direction = scanner.nextInt();
        graph = new int[n][m];

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                graph[i][j] = scanner.nextInt();
            }
        }

        operate(row, col, direction);

        System.out.println(count);
    }

    private static void operate(int row, int col, int direction) {
        // 1. 현 위치 청소
        clean(row, col);

        // 2. 왼쪽 방향부터 차례대로 탐색 진행
        for (int i = 0; i < 4; i++) {
            direction = rotate(direction);
            int nextRow = row + dr[direction];
            int nextCol = col + dc[direction];

            if (isLocation(nextRow, nextCol) && graph[nextRow][nextCol] == 0) {
                operate(nextRow, nextCol, direction);
                return;
            }
        }

        // 3. 네 방향 모두 청소가 되어 있거나 벽인 경우
        reverse(row, col, direction);
    }

    private static void clean(int row, int col) {
        if (graph[row][col] != 2) {
            graph[row][col] = 2;
            count++;
        }
    }

    private static int rotate(int direction) {
        direction--;
        if (direction == -1) {
            direction = 3;
        }
        return direction;
    }

    private static void reverse(int row, int col, int direction) {
        int backDirection = (direction + 2) % 4;
        int backRow = row + dr[backDirection];
        int backCol = col + dc[backDirection];
        if (isLocation(backRow, backCol) && graph[backRow][backCol] != 1) {
            operate(backRow, backCol, direction);
        }
    }

    private static boolean isLocation(int row, int col) {
        return row >= 0 && row < n && col >= 0 && col < m;
    }
}
#BOJ #구현 #시뮬레이션
last updated: 12/26/2021, 1:04:25 PM