[ 백준 ] 16236 / 아기 상어

金弘均·2021년 9월 15일
0

Baekjoon Online Judge

목록 보기
111/228
post-thumbnail

# Appreciation

/*
 * Problem :: 16236 / 아기 상어
 *
 * Kind :: BFS
 *
 * Insight
 * - BFS 한 번으로는 안될 것 같다...
 *   + 아기 상어의 위치에 따라 먹을 물고기가 계속 달라진다
 *   + 아기상어가 이동할 때마다 BFS 돌려도 시간 초과가 나지 않을려나?
 *     # O(전체칸의 개수 * BFS 한 번 돌리기) = O(20*20 * 20*20)
 *       = O(160000) < O(2*10^8)
 *       충분할 것 같다
 *
 * Point
 * - 아기 상어의 위치에 따른 물고기 선호도는 정렬로 해결하였다
 *   + BFS 하면서 만나는 첫 번째 먹을 수 있는 물고기가
 *     가장 위(또는 왼쪽)에 있지 않을 수도 있다!
 *   + 그냥 먹을 수 있는 물고기를 다 찾은 후에
 *     아기 상어의 입맛대로 정렬해서 먹어야 하는 물고기를 알아내자
 */

# Code

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

#include <iostream>
#include <queue>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

#define endl '\n'

// Set up : Global Variables
int N;
int space[20][20];
struct Point { int y, x; };
int dy[4] = {+1, 0, 0, -1};
int dx[4] = {0, +1, -1, 0};
struct BabyShark { Point pos; int size; int feed; } babyShark;
struct Fish { int dist; Point pos; };

// Set up : Functions Declaration
bool operator < (const Fish &u, const Fish &v);
bool isValid(int y, int x);
Fish bfs(Point s);


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

    // Set up : Input
    cin >> N;
    for (int i=0; i<N; i++) {
        for (int j=0; j<N; j++) {
            cin >> space[i][j];
            if (space[i][j] == 9) {
                babyShark.pos = {i, j}; /* 아기 상어 위치 초기화 */
                space[i][j] = 0; /* 아기 상어의 위치는 계속 추적할 테니까,
                                  * 그냥 초기 아기 상어의 위치는 빈칸으로 만들어둠 */
            }
        }
    }

    // Process
    babyShark.size = 2; /* 아기 상어 크기 초기화 */
    babyShark.feed = 0; /* 아기 상어 먹이 먹은 횟수 초기화 */
    int time = 0;
    while (true) {
        Fish target = bfs(babyShark.pos); /* 먹어야 할 물고기를 찾음 */
        if (target.dist == -1) break; /* 먹어야 할 물고기의 거리가 -1 이면
                                       * 즉, 먹을 수 있는 물고기가 없으면 종료 */

        time += target.dist; /* 물고기 거리만큼 흐른 시간 추가 */
        babyShark.pos = target.pos; /* 아기 상어의 위치를 물고기 위치로 갱신 */
        space[target.pos.y][target.pos.x] = 0; /* 아기상어가 물고기를 먹어서 빈칸이 됨 */
        babyShark.feed++; /* 아기 상어 먹이 먹은 횟수 증가 */
        /* 아기 상어 먹이 먹은 횟수와 크기가 같으면 */
        if (babyShark.feed == babyShark.size) {
            babyShark.size++; /* 아기 상어 사이즈업 */
            babyShark.feed = 0; /* 아기 상어 먹이 먹은 횟수 초기화 */
        }
    }

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

// Helper Functions
bool operator < (const Fish &u, const Fish &v)
/* 아기 상어의 입맛대로 물고기를 정렬하기 위한 비교 함수 */
{
    if (u.dist != v.dist) return u.dist < v.dist; /* 거리가 가까워야 됨 */
    if (u.pos.y != v.pos.y) return u.pos.y < v.pos.y; /* 똑같이 가까우면, 위에 있어야 됨 */
    return u.pos.x < v.pos.x; /* 똑같이 가깝고 위에 있으면, 왼쪽에 있어야 됨 */
}

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

Fish bfs(Point s)
/* 먹어야 하는 물고기의 정보(거리와 위치)를 반환,
 * 먹을 수 있는 물고기가 없다면 (int)-1 로 채워진 물고기를 반환 */
{
    queue<Point> que;
    bool isVisited[N][N];
    memset(isVisited, false, sizeof(isVisited));
    vector<Fish> fishes; /* 여기에 먹을 수 있는 물고기 정보가 저장됨 */

    que.push(s);
    isVisited[s.y][s.x] = true;

    int dist = -1;
    while (not(que.empty())) {
        int size = que.size();
        dist++;
        while (size--) {
            Point c = que.front(); que.pop();
            /* 빈칸이 아니고, 상어의 크기보다 작은 물고기가 있으면 */
            if (space[c.y][c.x] != 0 && space[c.y][c.x] < babyShark.size) {
                /* 일단 물고기 정보를 저장 */
                fishes.push_back({dist, c});
            }

            for (int i=0; i<4; i++) {
                int ny = c.y + dy[i];
                int nx = c.x + dx[i];
                /* 좌표가 유효하고, 방문하지 않았고,
                 * 빈칸이거나 상어의 크기보다 같거나 작은 물고기가 있으면 */
                if (isValid(ny, nx) && not(isVisited[ny][nx])
                    && space[ny][nx] <= babyShark.size)
                {
                    /* 이동가능 */
                    isVisited[ny][nx] = true; /* 방문 표시 */
                    que.push({ny, nx}); /* Queue 에 좌표를 넣어줌 */
                }
            }
        }
    }

    /* 먹을 수 있는 물고기가 없으면 */
    if (fishes.empty())
        /* (int)-1 로 채워진 물고기 정보 반환 */
        return {-1, {-1, -1}};
    /* 먹을 수 있는 물고기가 있으면 */
    else {
        /* 아기 상어 입맛대로 정렬 */
        sort(fishes.begin(), fishes.end());
        /* 가장 앞의 물고기 정보(이 물고기가 아기 상어가 먹을 물고기)를 반환 */
        return fishes.front();
    }
}
profile
이런 미친 게임을 봤나! - 옥냥이

0개의 댓글