본문 바로가기

백준

[C++ 백준 삼성 SW 역량 기출 문제 - 20057 마법사 상어와 토네이도]

728x90

역시나 구현 문제는 매우 복잡하기 때문에, 실수가 생기면 그걸 찾는 데 시간이 더 걸린다.
따라서 처음부터 실수를 하지 않도록 주의해야 한다. 하지만 이번에도 실수를 해버렸고, 나와 비슷한 실수를 한 사람의 질문 글로 인해 찾을 수 있었다. 
<토네이도의 움직임>
N = 5인 경우 
- 서1, 남1, 동2, 북2, 서 3, 남3, 동4, 북4, 서4
- 동서남북 방향은 계속 바뀐다. 
- 각 숫자는 두번씩 반복된다. 
- 마지막 숫자만 세번 반복된다. 

<확산 알고리즘> 
- 하나의 함수로 처리하기 보다는, 동서남북 각 경우의 10가지 확산 방향을 배열에 저장하여 사용하였다. (여기서 실수가 나왔다.)

<주의할 점>
- 남는 모래가 '알파'로 간다고 해서, 처음부터 '알파'의 비율을 구하려고 하면 안된다. '나머지 비율'로 확산하는 모래는 소수가 아니라 정수이기 때문이다. 따라서 먼저 '나머지 비율'로 확산되는 모래의 총량을 구하고, Y 위치에서 그 총량을 뺀 값을 '알파'에 더해주어야 한다. 

- 격자 밖으로 나간 모래의 양을 구해야 하므로 배열의 크기는 N보다 상하좌우로 2만큼 커야 한다. (확산될 때 Y의 위치에서 최대 2만큼 떨어진 거리로 모래가 퍼지므로) => 입력 받을 때, 시작점을 구할 때도 이 점을 유의해야 한다. 
#include <stdio.h>
#include <vector>

using namespace std;

const int dy[4] = {0, 1, 0, -1};    //서 남 동 북 
const int dx[4] = {-1, 0, 1, 0};
int map[503][503] = {0, };
int n;
int total = 0; 
//확산 비율
const int per[9] = {1, 1, 2, 7, 7, 2, 10, 10, 5};

//x기준에서 상대 위치
const int pos_y[4][10] = {{-1, 1, -2, -1, 1, 2, -1, 1, 0, 0}, 
                        {0, 0, 1, 1, 1, 1, 2, 2, 3, 2}, 
                        {-1, 1, -2, -1, 1, 2, -1, 1, 0, -2}, 
                        {0, 0, -1, -1, -1, -1, -2, -2, -3, 0}};

const int pos_x[4][10] = {{0, 0, -1, -1, -1, -1, -2, -2, -3, -2}, 
                        {-1, 1, -2, -1, 1, 2, -1, 1, 0, 0}, 
                        {0, 0, 1, 1, 1, 1, 2, 2, 3, 0}, 
                        {-1, 1, -2, -1, 1, 2, -1, 1, 0, 2}};

int y;
int x;

void spread(int direction){
    //x위치
    int pre_y = y;
    int pre_x = x;

    //y위치
    y += dy[direction];
    x += dx[direction];
    int tot_temp = 0;

    //확산
    for (int i = 0; i < 9; ++i){
        int plus = map[y][x] * per[i] / 100;
        map[pre_y + pos_y[direction][i]][pre_x + pos_x[direction][i]] += plus;
        tot_temp += plus;
    }
    //알파채우기
    map[y + dy[direction]][x + dx[direction]] += (map[y][x] - tot_temp); 

    //모두 확산 뒤 소멸
    map[y][x] = 0;
}


int main(){
    scanf("%d", &n);
    for (int i = 2; i < 2 + n; ++i){
        for (int j = 2; j < 2 + n; ++j){
            scanf("%d", &map[i][j]);
        }
    }

    int start = 0;
    y = n / 2 + 2;
    x = n / 2 + 2;

    for (int i = 1; i <= n - 1; ++i){
        for (int j = 0; j < 3; ++j){
            if (i != n - 1 && j == 2){
                continue;
            }
            for (int k = 0; k < i; ++k){
                int dir = start % 4;
                if (dir == 4){
                    dir = 0;
                }
                // 확산
                spread(dir);
            }
            ++start;
        }    
    }
    
    //격자 밖 모래 계산
    for (int i = 0; i < n + 4; ++i){
        for (int j = 0; j < n + 4; ++j){
            if (i >= 2 && i < n + 2 && j >= 2 && j < n + 2){
                continue;
            }
            total += map[i][j];
        }
    }

    printf("%d", total);
}