본문으로 바로가기

[BOJ 14503] 로봇 청소기(C++)

category Algorithms/Implementation 2021. 10. 1. 12:42

로봇 청소기(Gold 5)

문제

전체 문제 보기

 

14503번: 로봇 청소기

로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오. 로봇 청소기가 있는 장소는 N×M 크기의 직사각형으로 나타낼 수 있으며, 1×1크기의 정사각형 칸으로 나누어

www.acmicpc.net

접근법

이번 문제는 지문에서 요구한 로봇 청소기의 기능을 하나씩 구현하여 시뮬레이션하는 문제입니다. 자칫 DFS로 착각할 수 있는데 지문에서 요구한 로봇의 움직임은 DFS와 다르기 때문에 다른 결과가 나옵니다. 그래서 지문에 나온 로봇청소기의 동작을 정확하게 구현하는 것이 중요합니다. 지문에 제시된 로봇 청소기의 기능들을 클래스로 다음과 같이 추상화할 수 있습니다.

using Level = vector<vector<int>>;
struct Pos {
    Pos() {}
    Pos(int _r, int _c) : r(_r), c(_c) {}
    int r{};
    int c{};
};
class Robot {
public:
    Robot(Level& _level, Pos _pos, int _dir);
    void MoveForward();  // 앞으로 이동
    bool MoveBackward(); // 뒤로 이동 (뒤에 벽이 있을 경우 false 반환)
    void RotateLeft();   // 왼쪽 방향으로 회전
    void Clean();        // 현재 위치 청소
    bool Search();       // 바라보고 있는 방향이 0인지 확인
    Pos GetPos() const { return pos; }
    int GetCleanCnt() const { return cleanCnt; }
private:
    Level& level;
    Pos pos;
    int dir;
    int cleanCnt{};
    const int dr[4]{ -1,0,1,0 };
    const int dc[4]{ 0,1,0,-1 };
};

지문에서 로봇청소기의 동작 순서를 다음과 같이 설명하고 있습니다.

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

이를 앞서 정의한 로봇 청소기의 기능들을 활용하여 아래와 같이 로직을 구현할 수 있습니다.

    while (1)
    {
        // (청소할 수 있다면) 현재 위치를 청소한다.
        Pos cur = robot.GetPos();
        if(level[cur.r][cur.c] == 0)
            robot.Clean();

        bool Search{};
        // 현재 방향을 기준으로 왼쪽 방향 부터 차례대로 인접한 칸들을 탐색한다.
        for(int i = 0; i < 4; ++i)
        {
            robot.RotateLeft();
            if (robot.Search()) 
            {
                // 청소할 공간이 존재한다면 전진후 처음 부터 다시 진행한다.
                robot.MoveForward();
                Search = true;
                break;
            }
        }
        // 4방향 모두 청소 할 수 없는 경우 뒤로 이동한다
        if (Search == false)
        {
            // 뒤로 이동 할 수 없을 경우 청소 종료
            if (robot.MoveBackward() == false)
                break;
        }
    }

전체 구현

앞서 설명한 로봇 청소기의 기능들을 아래와 같이 모두 구현하면 답을 계산할 수 있습니다.

#include <stdio.h>
#include <vector>
using namespace std;

using Level = vector<vector<int>>;
struct Pos {
    Pos() {}
    Pos(int _r, int _c) : r(_r), c(_c) {}
    int r{};
    int c{};
};
class Robot {
public:
    Robot(Level& _level, Pos _pos, int _dir);
    void MoveForward();
    bool MoveBackward();
    void RotateLeft();
    void Clean();
    bool Search();
    Pos GetPos() const { return pos; }
    int GetCleanCnt() const { return cleanCnt; }
private:
    Level& level;
    Pos pos;
    int dir;
    int cleanCnt{};
    const int dr[4]{ -1,0,1,0 };
    const int dc[4]{ 0,1,0,-1 };
};
Robot::Robot(Level& _level, Pos _pos, int _dir) : level(_level), pos(_pos), dir(_dir)
{

}
void Robot::MoveForward()
{
    pos.r += dr[dir];
    pos.c += dc[dir];
}
bool Robot::MoveBackward()
{
    pos.r -= dr[dir];
    pos.c -= dc[dir];
    if (level[pos.r][pos.c] == 1)
        return false;

    return true;
}
void Robot::RotateLeft()
{
    dir = (dir + 3) % 4;
}
void Robot::Clean()
{
    level[pos.r][pos.c] = 2;
    cleanCnt++;
}
bool Robot::Search()
{
    int nextR = pos.r + dr[dir];
    int nextC = pos.c + dc[dir];

    if (level[nextR][nextC] == 0)
        return true;

    return false;
}
int main() {

    int N, M; // N, M  (3 ≤ N, M ≤ 50)
    scanf("%d %d ", &N, &M);
    int r, c, d; // d (0: 북쪽, 1: 동쪽, 2: 남쪽, 3: 서쪽)
    scanf("%d %d %d ", &r, &c, &d);

    Level level(N);
    for (int i = 0; i < N; ++i)
    {
        level[i].resize(M);
        for (int j = 0; j < M; ++j)
        {
            scanf("%d ", &level[i][j]);
        }
    }

    Robot robot(level, Pos(r, c), d);

    while (1)
    {
        Pos cur = robot.GetPos();
        if(level[cur.r][cur.c] == 0)
            robot.Clean();

        bool Search{};

        for(int i = 0; i < 4; ++i)
        {
            robot.RotateLeft();
            if (robot.Search()) 
            {
                robot.MoveForward();
                Search = true;
                break;
            }
        }
        // 4방향 모두 청소 할 수 없는 경우
        if (Search == false)
        {
            // 뒤로 이동 할 수 없을 경우 청소 종료
            if (robot.MoveBackward() == false)
                break;
        }
    }
    printf("%d\n", robot.GetCleanCnt());

    return 0;
}