[ 백준 ] 16946 / 벽 부수고 이동하기 4

金弘均·2021년 9월 15일
0

Baekjoon Online Judge

목록 보기
110/228
post-thumbnail
post-custom-banner

# Appreciation

/*
 * Problem :: 16946 / 벽 부수고 이동하기 4
 *
 * Kind :: BFS + Dynamic Programming
 *
 * Insight
 * - 칸 중에 벽인 곳은 빈칸으로 바꾸고 그 칸에서 매번 BFS 를 하려고 했는데...
 *   이 경우, 같은 빈 칸을 또 BFS 하게 된다 (이 방식으로 알고리즘을 짜면 시간초과가 뜬다)
 *
 * - 벽칸에서 인접한 칸만으로 빈칸의 개수를 알 수 있지 않을까?
 *   + 11001 => 빈칸 넘버링 00110 => No. 1 : 2
 *     00111             22000    No. 2 : 3
 *     01010             20304    No. 3 : 1
 *     10101             05060    No. 4 : 1
 *                                No. 5 : 1
 *   + BFS 돌때, 빈칸 넘버링과 동시에 그 넘버에 해당하는 값을 저장한다면
 *     나중에 벽칸근처의 빈칸의 개수를 그 칸에 인접한 빈칸의 넘버링만 알면 알 수 있다
 *
 * Point
 * - 벽칸에 인접한 빈칸의 넘버가 중복될 수 있다
 *   Set 으로 이러한 경우를 다루었다
 */

# Code

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

#include <iostream>
#include <set>
#include <map>
#include <queue>

using namespace std;

#define endl '\n'

// Set up : Global Variables
int N, M;
char board[1000][1000];
int label[1000][1000];
bool isVisited[1000][1000];
struct Point { int y, x; };
int dy[4] = {+1, 0, 0, -1};
int dx[4] = {0, +1, -1, 0};

// Set up : Functions Declaration
bool isValid(int y, int x);
int bfs(int sy, int sx, int No);


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

    // Set up : Input
    cin >> N >> M;
    for (int i=0; i<N; i++) {
        for (int j=0; j<M; j++) {
            cin >> board[i][j];
        }
    }

    // Process
    /* cnt[넘버] = 그 넘버에 해당하는 빈칸의 개수 */
    map<int,int> cnt;
    int No = 0;
    for (int i=0; i<N; i++) {
        for (int j=0; j<M; j++) {
            /* 방문하지 않은 칸이고 빈칸이면 */
            if (not(isVisited[i][j]) && board[i][j] == '0') {
                /* BFS 를 돌면서 넘버링 해줌, 그 넘버에 해당하는 빈칸의 개수도 저장 */
                cnt[No] = bfs(i, j, ++No);
            }
        }
    }

    // Control : Output
    for (int i=0; i<N; i++) {
        for (int j=0; j<M; j++) {
            /* 빈칸이면 */
            if (board[i][j] == '0') {
                /* '0' 을 출력 */
                cout << 0;
            /* 벽이면 */
            } else {
                set<int> labels; /* 여기다 인접한 칸의 넘버값을 넣어준다 */
                for (int k=0; k<4; k++) {
                    int ay = i + dy[k];
                    int ax = j + dx[k];
                    if (isValid(ay, ax)) {
                        labels.insert(label[ay][ax]);
                    }
                }
                int s = 1; /* 이동할 수 있는 칸은 자신의 칸도 포함 */
                for (int l : labels) {
                    /* 넘버에 해당하는 빈칸의 개수를 더해줌 */
                    s += cnt[l];
                } s %= 10;
                cout << s;
            }
        } cout << endl;
    } cout << endl;
}

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

int bfs(int sy, int sx, int No)
/* (sy, sx) 를 기준으로 BFS 를 돎, 빈칸의 개수를 반환 */
{
    queue<Point> que;

    que.push({sy, sx});
    isVisited[sy][sx] = true;

    int size = 0;
    while (not(que.empty())) {
        Point c = que.front(); que.pop(); /* Queue 에서 원소를 꺼낼때마다 */
        label[c.y][c.x] = No; /* 넘버링해주고 */
        size++; /* 빈칸의 개수 증가시켜줌 */

        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]) && board[ny][nx] == '0') {
                /* 방문처리 하고 */
                isVisited[ny][nx] = true;
                /* Queue 에 넣음 */
                que.push({ny, nx});
            }
        }
    }

    return size;
}
profile
이런 미친 게임을 봤나! - 옥냥이
post-custom-banner

0개의 댓글