[ 백준 ] 2174 / 로봇 시뮬레이션

31727 단어 psbojboj

# Appreciation

/*
 * Problem :: 2174 / 로봇 시뮬레이션
 *
 * Kind :: Simulation
 *
 * Insight
 * - 의외로 스펙이 좀 많다...?
 *   고려해야할 점도 많다...?
 *   실수할 수 있는 부분이 꽤 있다...?
 *   + 일단, 주어진 땅에서 y 좌표가 아래서 위로 증가하는 것으로
 *     주어진 것은 크게 문제되지 않는다
 *     항상 y 좌표가 위에서 아래로 증가하는 식으로 풀었는데...
 *     땅을 위아래 뒤집어준다고 생각하면 된다
 *   + 로봇들의 데이터를 저장하는 전역배열이 필요하다
 *     로봇의 번호, 좌표, 방향을 항상 추적해야 한다
 *   + 방향을 바꾸어주는 명령같은 경우
 *     이를 따로 처리하는 함수를 만들기 보다는
 *     열거형과 전역배열을 사용해서 처리해주자
 *   + 로봇의 데이터를 변수로 선언해서 가져와야할 때
 *     참조변수로 선언해서 다루어주자
 *   + 몇 번 로봇이 또 다른 몇 번 로봇과 충돌했는지 쉽게 파악하기 위해서
 *     로봇들의 위치를 저장하는 2차원전역배열이 필요하다
 *     # 위의 점들을 생각하며 구현해주자...
 *       -> 하드코딩은 안 한것 같은데 왜이리 코드가 길다냐 ㅠㅠ
 *
 * Point
 * - 아무래도 명령을 수행하는 함수를 잘 만드는게 이 문제를 푸는데 있어 핵심이다
 *   + 이번에 풀었던 코드에서는 명령을
 *     (명령을 받는 로봇, 명령의 종류, 명령의 반복 횟수) 로 보아서
 *     그 함수 안에서 반복까지 시켜주었는데
 *     처음 풀었던 코드에서는 명령을 (명령을 받는 로봇, 명령의 종류) 로 보고
 *     밖의 함수에서 반복시켜주었다
 *     # 최적화면에서는 전자의 경우가 <= 함수 호출 횟수가 적음
 *       쉬운구현면에서는 후자의 경우가 <= F 명령 구현이 더 쉬움
 *       더 좋은 것 같다
 *
 * - 좌표의 사용을
 *   구조체에는 x,y 순서
 *   함수에서 사용은 y,x 순서로 했더니
 *   헷갈려서 좀 틀렸다... 다음부터는 문제의 양식보다는 일관성을 더 중요하게 다뤄야지
 */

# Code

//
//  BOJ
//  ver.C++
//
//  Created by GGlifer
//
//  Open Source

#include <iostream>
#include <algorithm>

using namespace std;

#define endl '\n'

// Set up : Global Variables
int A, B; /* 가로의 길이, 세로의 길이 */
int N, M; /* 로봇의 개수, 명령의 개수 */
int T[100+1][100+1]; /* T[i][j] = (i,j) 에 있는 로봇의 번호, 없으면 0 */
struct Robot { int no, x, y, dir; }; /* 구조체 Robot : 로봇의 데이터 */
Robot R[100+1]; /* R[i] = i 번 로봇의 데이터 */
struct Command { int no; char type; int rep; }; /* 구조체 Command : 명령 데이터 */
Command C[100+1]; /* C[i] = i 번째 명령 데이터 */
enum Direction { NORTH, WEST, EAST, SOUTH };
int dy[4] = {+1, 0, 0, -1}; /* 차례로 북쪽, 서쪽, 동쪽, 남쪽으로 이동시 y 좌표의 증감 */
int dx[4] = {0, -1, +1, 0}; /* 차례로 북쪽, 서쪽, 동쪽, 남쪽으로 이동시 x 좌표의 증감 */
enum Rotate { LEFT, RIGHT };
int D[4][2] = {
        {WEST, EAST},       /* 북쪽의 왼쪽 = 서쪽, 북쪽의 오른쪽 = 동쪽 */
        {SOUTH, NORTH},     /* 서쪽의 왼쪽 = 남쪽, 서족의 오른쪽 = 북쪽 */
        {NORTH, SOUTH},     /* 동쪽의 왼쪽 = 북쪽, 동쪽의 오른쪽 = 남쪽 */
        {EAST, WEST}        /* 남쪽의 왼쪽 = 동쪽, 남쪽의 오른쪽 = 서족 */
};
struct Result { bool status; int X, Y; }; /* 구조체 Result : 명령 실행 결과 데이터 */
#define NONE (-1) /* 구조체 Result 에 쓰이는 상수 */
#define WALL 0 /* 구조체 Result 에 쓰이는 상수 */
#define ERROR false /* 구조체 Result 에 쓰이는 상수 */

// Set up : Functions Declaration
bool isValid(int y, int x);
Result exe(Command cmd);
Result typeL(Command cmd);
Result typeR(Command cmd);
Result typeF(Command cmd);


int main()
{
    // Set up : I/O
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // Set up : Input
    cin >> A >> B;
    cin >> N >> M;
    for (int i=1; i<=N; i++) {
        int x, y; char d;
        cin >> x >> y >> d;
        int dir;
        switch (d) {
            case 'N': dir = 0; break;
            case 'W': dir = 1; break;
            case 'E': dir = 2; break;
            case 'S': dir = 3; break;
            default: throw runtime_error("Invalid Direction");
        }
        R[i] = {i, x, y, dir};
        T[y][x] = i;
    }
    for (int i=1; i<=M; i++) {
        int no; char type; int rep;
        cin >> no >> type >> rep;
        C[i] = {no, type, rep};
    }

    // Process
    bool isThereError = false;
    Result res{}; /* 명령의 실행 결과 */
    for (int i=1; i<=M; i++) {
        res = exe(C[i]);
        /* 결과의 상태가 ERROR 라면 */
        if (res.status == ERROR) {
            isThereError = true;
            break;
        }
    }

    // Control : Output
    if (not(isThereError))
        cout << "OK" << endl;
    else {
        /* 결과의 Y 값이 벽이라면 */
        (res.Y == WALL) ? /* 삼항 연산자 사용 */
        /* 로봇이 벽에 충돌한 경우 */
        printf("Robot %d crashes into the wall\n", res.X) :
        /* 로봇이 다른 로봇과 충돌한 경우 */
        printf("Robot %d crashes into robot %d\n", res.X, res.Y);
    }
}

// Helper Functions
bool isValid(int y, int x)
/* 주어진 좌표 (y,x) 가 유효하면 true 를 반환, 그 외 false 를 반환 */
{
    return y >= 1 && y <= B && x >= 1 && x <= A;
}

Result exe(Command cmd)
/* 명령 cmd 를 실행한 결과를 반환 */
{
    /* 명령의 type 에 따라 적절한 수행 함수를 호출 */
    char type = cmd.type;
    switch (type) {
        case 'L': return typeL(cmd);
        case 'R': return typeR(cmd);
        case 'F': return typeF(cmd);
        default: throw runtime_error("Invalid Type");
    }
}

Result typeL(Command cmd)
/* 타입 L 명령을 실행하고 그 결과를 반환 */
{
    Robot &r = R[cmd.no];
    for (int i=0; i<cmd.rep; i++) {
        r.dir = D[r.dir][LEFT];
    } return {true, NONE, NONE};
}

Result typeR(Command cmd)
/* 타입 R 명령을 실행하고 그 결과를 반환 */
{
    Robot &r = R[cmd.no];
    for (int i=0; i<cmd.rep; i++) {
        r.dir = D[r.dir][RIGHT];
    } return {true, NONE, NONE};
}

Result typeF(Command cmd)
/* 타입 F 명령을 실행하고 그 결과를 반환 */
{
    Robot &r = R[cmd.no];
    int cy = r.y, cx = r.x; /* 처음 로봇의 위치 */
    for (int i=0; i<cmd.rep; i++) {
        /* 현재 로봇이 바라보는 방향으로 한 칸 이동 */
        int ny = cy + dy[r.dir];
        int nx = cx + dx[r.dir];
        /* 이동한 칸이 주어진 땅의 밖으로 벗어난 칸이라면 */
        if (not(isValid(ny, nx)))
            /* 결과 : 에러발생, 충돌한 로봇 번호, 벽 */
            return {ERROR, r.no, WALL};
        /* 이동한 칸에 다른 로봇이 있다면 */
        if (T[ny][nx] != 0)
            /* 결과 : 에러발생, 충돌한 로봇 번호, 충돌당한 로봇 번호 */
            return {ERROR, r.no, T[ny][nx]};
        cy = ny, cx = nx; /* 로봇의 현재 위치 갱신 */
    }
    swap(T[r.y][r.x], T[cy][cx]); /* 땅에서 로봇의 현재 위치 갱신 및 저장 */
    r.y = cy, r.x = cx; /* 로봇의 현재 위치 저장 */
    return {true, NONE, NONE};
}

좋은 웹페이지 즐겨찾기