[ 백준 ] 17144 / 미세먼지 안녕!

金弘均·2021년 9월 16일
0

Baekjoon Online Judge

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

# Appreciation

/*
 * Problem :: 17144 / 미세먼지 안녕!
 *
 * Kind :: Simulation
 *
 * Insight
 * - 문제가 막 어렵지 않은데...
 *   구현이 좀 까다롭다
 *   + 1. 미세먼지 확산
 *     2. 미세먼지 청소
 *     이 두 과정을 구현해야 한다
 *
 * - 미세먼지 확산
 *   + 모든 미세먼지의 확산이 동시에 일어난다
 *     # 옆칸의 미세먼지가 확산되었을 때, 현재칸의 미세먼지의 양이 늘어나면 안된다
 *       ^= 확산되는 미세먼지 양이 달라지기 때문에
 *       -> 현재 방안의 미세먼지 양을 저장하는 배열과
 *          확산된 방안의 미세먼지 양을 저장하는 배열을 구분해주자
 *
 * - 미세먼지 청소
 *   + 북쪽의 순환영역과 남쪽의 순환영역을 나누어 구현해준다
 *     # 공기청정기안으로 들어오는 쪽의 값부터 한칸씩 이동해준다
 *       -> 다른 쪽 방향의 순환을 먼저 실행하면 값의 손실이 일어나게 된다
 *          북쪽의 순환영역 기준, O : 공기청정기 윗부분
 *          <----
 *          |   ^
 *          V   |
 *          O--->
 *          에서 아래쪽 방향이 아닌 왼쪽 방향부터 한칸씩 값을 이동시켜주면
 *          아래쪽 방향과 왼쪽방향이 만나는 코너에 있는 값의 손실이 일어나게 된다
 *       -> 사실은, 먼저 이동시키는 방향과 그 방향에 닿아있는 방향의 코너에 있는 값의 손실이
 *          필연적으로 일어나게 되는데 <= 따로 저장해두면 상관없겠지만
 *          공기청정기에 있는 코너의 값은 어차피 정화되어 0 이 될테니 손실되어도 상관없다
 *       -> 공기청정기 안으로 들어오는 쪽의 방향부터 꼬리를 무는순서대로 값을 이동시켜주자
 *
 * Point
 * - for 문 하드코딩...
 *   더 좋은 방법이 생각나지 않아 ㅠㅠ
 *   + 배열로 순환순서를 넣어놀까 싶었는데
 *     그건 그것대로 까다로워서...
 *
 * - 확산된 방안의 미세먼지 양을 구할 때
 *   현재 칸의 미세먼지 양은 인접한 칸들의 영향을 받기 때문에
 *   '=' 가 아닌 '+=' 로 해주어야 한다!!!
 *   
 * - 확산된 방안의 상태를 저장하는 배열을 만들 때,
 *   값뿐만 아니라 공기청정기 위치도 초기화해주자
 */

# Code

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

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

#define endl '\n'

// Set up : Global Variables
int R, C, T;
int board[50][50];
struct Point { int y, x; };
vector<Point> Cleaner;
int dy[4] = {+1, 0, 0, -1};
int dx[4] = {0, +1, -1, 0};
#define CLEANER (-1)

// Set up : Functions Declaration
bool isValid(int y, int x);
void spread();
void clean();


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

    // Set up : Input
    cin >> R >> C >> T;
    for (int i=0; i<R; i++) {
        for (int j=0; j<C; j++) {
            cin >> board[i][j];
            if (board[i][j] == -1) {
                Cleaner.push_back({i, j});
            }
        }
    }

    // Process
    while (T--) {
        spread(); /* 미세먼지 확산 */
        clean(); /* 미세먼지 청소 */
    }

    int ans = 0; /* T 초가 지난 후 미세먼지 총량 */
    for (int i=0; i<R; i++) {
        for (int j=0; j<C; j++) {
            if (board[i][j] > 0) { /* 미세먼지가 있으면 */
                ans += board[i][j]; /* 총량에 더해줌 */
            }
        }
    }

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

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

void spread()
/* 미세먼지 확산을 구현한 함수 */
{
    /* int boaard[50][50] = 현재 방안의 상태 */
    int update[50][50]; /* 확산된 방안의 상태 */
    memset(update, 0, sizeof(update)); /* 미세먼지 양 초기화 */
    Point n = Cleaner.front(), s = Cleaner.back();
    update[n.y][n.x] = CLEANER; /* 공기청정기 위치 초기화 */
    update[s.y][s.x] = CLEANER;

    for (int i=0; i<R; i++) {
        for (int j=0; j<C; j++) {
            if (board[i][j] > 0) { /* 미세먼지가 있으면 */
                int res = board[i][j]; /* 미세먼지 양 */
                int amt = res / 5; /* 확산되는 양 */
                int cnt = 0; /* 확산된 횟수 */
                for (int k=0; k<4; k++) {
                    int ay = i + dy[k];
                    int ax = j + dx[k];
                    /* 인접한 칸이 유효하고, 공기청정기가 아니면 */
                    if (isValid(ay, ax) && board[ay][ax] != CLEANER) {
                        /* 확산 */
                        cnt++;
                        update[ay][ax] += amt;
                    }
                }
                /* 확산되고 남은 양 */
                update[i][j] += res - cnt * amt;
            }
        }
    }

    /* 현재 방안의 상태를 확산된 방안의 상태로 갱신 */
    memcpy(board, update, sizeof(board));
}


void clean()
/* 미세먼지 청소를 구현한 함수 */
{
    /* 북쪽 순환 영역 */
    Point n = Cleaner.front(); /* 공기청정기 윗부분 */
    int ny = n.y, nx = n.x; /* 공기청정기 윗부분의 y 좌표, x 좌표 */
    for (int i=ny-2; i>=0; i--) /* 북쪽 순환 영역 아래쪽 방향 <= 공기청정기로 들어오는 방향 */
        board[i+1][0] = board[i][0];
    for (int j=1; j<C; j++) /* 북쪽 순환 영역 왼쪽 방향 */
        board[0][j-1] = board[0][j];
    for (int i=1; i<=ny; i++) /* 북쪽 순환 영역 위쪽 방향 */
        board[i-1][C-1] = board[i][C-1];
    for (int j=C-2; j>=1; j--) /* 북쪽 순환 영역 오른쪽 방향 <= 공기청정기로부터 나가는 방향 */
        board[ny][j+1] = board[ny][j];
    board[ny][nx+1] = 0; /* 공기청정기 윗부분 오른쪽 칸은 정화되어 미세먼지 양이 0 이 됨 */

    /* 남쪽 순환 영역 */
    Point s = Cleaner.back(); /* 공기청정기 아랫부분 */
    int sy = s.y, sx = s.x; /* 공기청정기 아랫부분의 y 좌표, x 좌표 */
    for (int i=sy+2; i<R; i++) /* 남쪽 순환 영역 위쪽 방향 <= 공기청정기로 들어오는 방향 */
        board[i-1][0] = board[i][0];
    for (int j=1; j<C; j++) /* 남쪽 순환 영역 왼쪽 방향 */
        board[R-1][j-1] = board[R-1][j];
    for (int i=R-2; i>=sy; i--) /* 남쪽 순환 영역 아래쪽 방향 */
        board[i+1][C-1] = board[i][C-1];
    for (int j=C-2; j>=1; j--) /* 남쪽 순환 영역 오른쪽 방향 */
        board[sy][j+1] = board[sy][j];
    board[sy][sx+1] = 0; /* 공기청정기 아랫부분 오른쪽 칸은 정화되어 미세먼지 양이 0 이 됨 */
}
profile
이런 미친 게임을 봤나! - 옥냥이
post-custom-banner

0개의 댓글