[ 백준 ] 17370 / 육각형 우리 속의 개미

金弘均·2021년 9월 16일
0

Baekjoon Online Judge

목록 보기
152/228
post-thumbnail

# Appreciation

/*
 * Problem :: 17370 / 육각형 우리 속의 개미
 *
 * Kind :: DFS + Backtracking
 *
 * Insight
 * - 개미의 현재 위치와 그 위치로 들어온 방향에 따라
 *   이동할 수 있는 방향이 달라진다
 *   + 일단 육각형의 각 꼭짓점에 번호를 매기자
 *         O <= 1
 *        / \
 *       -   -
 *      /     \
 *     O <= 2  O <= 3
 *     |       |
 *     |       |
 *     O <= 4  O <= 5
 *      \     /
 *       -   -
 *        \ /
 *         O <- 6
 *   + 위, 아래, 왼쪽, 오른쪽을 각각 U, D, L, R 이라고 하면
 *     개미가 이동할 수 있는 방향은 총 6가지다
 *     UU (4->2, 5->3)
 *     UR (2->1, 6->5)
 *     UL (3->1, 6->4)
 *     DD (2->4, 3->5)
 *     DR (1->3, 4->6))
 *     DL (1->2, 5->6)
 *     # 이를 조합하면,
 *       이전 방향 =>  다음 가능한 방향 => 실제로 가능한 방향
 *          UU   =>   UL, UR, DD  =>    UL, UR
 *          UR   =>   UU, DR, DL  =>    UU, DR
 *          DR   =>   UR, DD, UL  =>    UR, DD
 *          DD   =>   DL, DR, UU  =>    DL, DR
 *          DL   =>   UL, DD, UR  =>    UL, DD
 *          UL   =>   UU, DL, DR  =>    UU, DL
 *       임을 알 수 있다
 *
 * - 경로를 탐색해야 한다
 *   + 모든 경로를 탐색해봐도 될까?
 *     2^22 = 4194304 < 10^8 이니 시간 제한내에 풀 수 있을 것 같다
 *     # BFS 로 풀어야 하나, DFS 로 풀어야 하나?
 *       모든 경로를 확인해야 되며 그때마다 방문처리를 적절히 해주어야 한다
 *       이전의 상태를 쉽게 알 수 있는 DFS 가 적합할 것 같다
 *       ^= 사실 BFS 로 풀려니 방문처리를 어떻게 다루어야 할지 잘 모르겠다...
 *          Backtracking 도 BFS 보다는 DFS 가 더 잘 맞는것 같은 느낌이 드는데...
 *
 * Point
 * - max(N)=22 이므로 위아래, 좌우 모두 44 만큼 움직일 수 있다고 생각하여
 *   그냥 개미가 움직이는 판을 (bool)isVisited[100][100] 으로 선언했다
 *   그러고선 개미는 (50,50) 에서 출발하는 것으로 생각하였다
 *   이러면, 개미가 판을 넘어가는 경우는 존재하지 않게 된다
 *   + 문제 설명에 '무한히 많은 정육각형이 서로 맞닿아 놓인 형태의 개미우리' 라고 적혀져 있으므로
 *     개미우리 벽을 넘어가는 유효하지 않은 좌표는 없다
 *     따라서 유효한 좌표검사를 신경쓰지 않으려고 개미가 움직이는 판을 크게 잡았다
 *
 * - 개미가 (50,50) 에서 이전 방향이 UU 인채로 시작한다고 가정하면
 *   현재 개미는 (48,50) 을 방문한 것이다
 *   따라서 isVisited[48][50] = true 로 설정하는 것을 잊지말자
 *   +  참고로 좌표계는
 *      오른쪽으로 갈수록 x 값이 커지고
 *      위쪽으로 갈수록 y 값이 커지는 것으로 설정했다
 */

# Code

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

#include <iostream>

using namespace std;

#define endl '\n'

// Set up : Global Variables
int N;
bool isVisited[100][100];
enum Direction { UU, UR, DR, DD, DL, UL }; /* for 가독성 */
int dy[6] = {+2, +1, -1, -2, -1, +1};
int dx[6] = { 0, +2, +2,  0, -2, -2};
int D[6][2] = {
        {UL, UR}, /* UU */
        {UU, DR}, /* UR */
        {UR, DD}, /* DR */
        {DL, DR}, /* DD */
        {UL, DD}, /* DL */
        {UU, DL}  /* UL */
};

// Set up : Functions Declaration
int dfs(int cy, int cx, int cnt, int prevDir);

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

    // Set up : Input
    cin >> N;

    // Process
    isVisited[48][50] = true; /* 시작 위치 방문 처리 */
    int ans = dfs(50, 50, 0, UU);

    // Control : Output
    cout << ans << endl;
}

// Helper Functions
int dfs(int cy, int cx, int cnt, int prevDir)
/* 현재 좌표인 (cy, cx) 에서 시작하여 (N-cnt) 만큼 회전하고 멈추는 경우의 수 반환 */
{
    /* 현재 좌표가 방문된 좌표이면 */
    if (isVisited[cy][cx])
        /* 방향 회전을 N번 하였으면 조건에 맞는 경로를 하나 찾았으므로 1을 반환,
         * 그렇지 않으면 0을 반환 */
        return (cnt == N) ? 1 : 0;
    /* 현재 좌표가 방문되지 않은 좌표인데 방향 회전을 N번 이상 하였으면 */
    if (cnt >= N)
        /* 이 경로는 조건에 맞는 경로가 될 수 없으므로 0을 반환 */
        return 0;

    isVisited[cy][cx] = true; /* 현재 좌표 방문 처리 */

    int ret = 0; /* 경우의 수 */
    for (int nextDir : D[prevDir]) {
        int ny = cy + dy[nextDir]; /* 탐색할 다음 y 좌표 */
        int nx = cx + dx[nextDir]; /* 탐색할 다음 x 좌표 */
        ret += dfs(ny, nx, cnt+1, nextDir); /* 경로 탐색 */
    }

    isVisited[cy][cx] = false; /* 현재 좌표 비(非)방문 처리 */

    return ret;
}
profile
이런 미친 게임을 봤나! - 옥냥이

0개의 댓글