---
title: Samsung sorftware Algorithms. 21610.마법사 상어와 비바라기
date: 2022-04-12
tags: algorithmsolutions, 공부, 탐색
excerpt: 21610.마법사 상어와 비바라기 - 삼성기출문제 (from.백준알고리즘)
---
# 21610.마법사 상어와 비바라기 - 삼성기출문제 (from.백준알고리즘)

해당 문제는 시뮬레이션 문제로 단계별로 수행하는 함수를 이용하여 풀 수 있는 문제입니다.

링크 :
[![image](/assets/img/algo_img/baekjoon_logo.png)](https://www.acmicpc.net/problem/21610)

---

## 1. 문제

<details>
<summary>문제 보기</summary>
<div markdown="1">

### 21610\. 마법사 상어와 비바라기

|시간 제한|메모리 제한|
|------|---|
|1 초 (추가 시간 없음)|1024 MB|

마법사 상어는 파이어볼, 토네이도, 파이어스톰, 물복사버그 마법을 할 수 있다. 오늘 새로 배운 마법은 비바라기이다. 비바라기를 시전하면 하늘에 비구름을 만들 수 있다. 오늘은 비바라기를 크기가 N×N인 격자에서 연습하려고 한다. 격자의 각 칸에는 바구니가 하나 있고, 바구니는 칸 전체를 차지한다. 바구니에 저장할 수 있는 물의 양에는 제한이 없다. (r, c)는 격자의 r행 c열에 있는 바구니를 의미하고, A[r][c]는 (r, c)에 있는 바구니에 저장되어 있는 물의 양을 의미한다.

격자의 가장 왼쪽 윗 칸은 (1, 1)이고, 가장 오른쪽 아랫 칸은 (N, N)이다. 마법사 상어는 연습을 위해 1번 행과 N번 행을 연결했고, 1번 열과 N번 열도 연결했다. 즉, N번 행의 아래에는 1번 행이, 1번 행의 위에는 N번 행이 있고, 1번 열의 왼쪽에는 N번 열이, N번 열의 오른쪽에는 1번 열이 있다.

비바라기를 시전하면 (N, 1), (N, 2), (N-1, 1), (N-1, 2)에 비구름이 생긴다. 구름은 칸 전체를 차지한다. 이제 구름에 이동을 M번 명령하려고 한다. i번째 이동 명령은 방향 d<sub>i</sub>과 거리 s<sub>i</sub>로 이루어져 있다. 방향은 총 8개의 방향이 있으며, 8개의 정수로 표현한다. 1부터 순서대로 ←, ↖, ↑, ↗, →, ↘, ↓, ↙ 이다. 이동을 명령하면 다음이 순서대로 진행된다.

1. 모든 구름이 d<sub>i</sub> 방향으로 s<sub>i</sub>칸 이동한다.
2. 각 구름에서 비가 내려 구름이 있는 칸의 바구니에 저장된 물의 양이 1 증가한다.
3. 구름이 모두 사라진다.
4. 2에서 물이 증가한 칸 (r, c)에 물복사버그 마법을 시전한다. 물복사버그 마법을 사용하면, 대각선 방향으로 거리가 1인 칸에 물이 있는 바구니의 수만큼 (r, c)에 있는 바구니의 물이 양이 증가한다.   
   - 이때는 이동과 다르게 경계를 넘어가는 칸은 대각선 방향으로 거리가 1인 칸이 아니다.
   - 예를 들어, (N, 2)에서 인접한 대각선 칸은 (N-1, 1), (N-1, 3)이고, (N, N)에서 인접한 대각선 칸은 (N-1, N-1)뿐이다.
5. 바구니에 저장된 물의 양이 2 이상인 모든 칸에 구름이 생기고, 물의 양이 2 줄어든다. 이때 구름이 생기는 칸은 3에서 구름이 사라진 칸이 아니어야 한다.

M번의 이동이 모두 끝난 후 바구니에 들어있는 물의 양의 합을 구해보자.

### 입력(Input)

첫째 줄에 N, M이 주어진다.

둘째 줄부터 N개의 줄에는 N개의 정수가 주어진다. r번째 행의 c번째 정수는 A[r][c]를 의미한다.

다음 M개의 줄에는 이동의 정보 d<sub>i</sub>, s<sub>i</sub>가 순서대로 한 줄에 하나씩 주어진다.


### 출력(Output)

첫째 줄에 M번의 이동이 모두 끝난 후 바구니에 들어있는 물의 양의 합을 출력한다.

### 제한

 - 2 ≤ N ≤ 50
 - 1 ≤ M ≤ 100
 - 0 ≤ A[r][c] ≤ 100
 - 1 ≤ d<sub>i</sub> ≤ 8
 - 1 ≤ s<sub>i</sub> ≤ 50

</div>
</details>

---

## 2. 문제 풀이

이 문제는 시뮬레이션 문제로, 나와있는 조건을 그대로 구현하기만 하면 된다.

먼저 구름의 위치를 가지고 있는 Queue를 만들어서 현재 위치 값을 가지고 있는 Cloud 객체를 만들고 큐에 넣는다.

그리고 이 구름을 이동시키는데, 여기서 중요한것은 양 옆에서 넘어갈 수 있도록 만들었다는것이다. 1에서 줄어들면 N으로 가는 방식을 2중, 3중 더 넘더라도 위치를 정확하게 찍어줘야 한다는것이다.   
때문에 이동하는 s<sub>i</sub>를 N의 나머지 부분으로 바꾸어서 이동하게 했다. 그리고 이동한 구름의 위치에 비가 내려 양동이의 양을 1 증가시킨다.   
이 때 중요한것은 이렇게 증가된 곳을 체크해주고 이후에 새로 구름이 생성될 때, 비가 내린 곳은 생성되지 않도록 한다.

그리고 물 복사 마법이라고 (r,c) 부분의 대각선 4부분을 체크해서 물이 있으면 그 물의 수만큼 (r,c)에 위치한 양동이의 물이 증가된다.   
이 액트는 격자의 범위를 넘어가면 카운트에서 제외시킨다.   
이 때 주의해야 할 부분이 기존 격자에서 체크해서 증가시키면 1이었던 양동이가 2가되어서 다른 양동이에 영향을 줄 수 있기 때문에 격자를 복사해서 해당 위치의 양동이만 증가될 수 있도록 해야 한다.   

이것을 코드로 표현하겠습니다.

```java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class MagicianShark_Windblows_21610 {
    static int N, M;
    static int[][] map;
    static boolean[][] rain;
    static int[][] dir = {  // 0은 빈칸, 좌, 좌상, 상, 우상, 우, 우하, 하, 좌하
        {0, 0, -1, -1, -1, 0, 1, 1, 1},
        {0, -1, -1, 0, 1, 1, 1, 0, -1}
    };
    static int[][] checkBucket = {
        {-1, -1, 1, 1},
        {-1, 1, -1, 1}
    };
    static int[][] command;
    static Queue<Cloud> cloud;
    public static void moveCloudnRain(int d, int s) {
        int size = cloud.size();

        rain = new boolean[N + 1][N + 1];
        while(size > 0) {
            Cloud tmp = cloud.poll();
            int nr = tmp.r + (dir[0][d] * (s % N));
            int nc = tmp.c + (dir[1][d] * (s % N));

            // 이어붙인 공간 넘어가기
            if(nr <= 0) nr = N + nr;
            if(nc <= 0) nc = N + nc;
            if(nr > N) nr = nr - N;
            if(nc > N) nc = nc - N;

            cloud.add(new Cloud(nr, nc));
            map[nr][nc]++;
            rain[nr][nc] = true;
            size--;
        }
    }
    public static void copywater() {
        int[][] copyMap = new int[N + 1][N + 1];
        for(int i = 1; i <= N; i++) {
            for(int j = 1; j <= N; j++) {
                copyMap[i][j] = map[i][j];
            }
        }

        while(!cloud.isEmpty()) {
            Cloud tmp = cloud.poll();
            rain[tmp.r][tmp.c] = true;

            // 물 복사 마법
            int cnt = 0;
            for(int i = 0; i < 4; i++) {
                int nr = tmp.r + checkBucket[0][i];
                int nc = tmp.c + checkBucket[1][i];
                if(nr <= 0 || nc <= 0 || nr > N || nc > N) continue;
                if(copyMap[nr][nc] > 0) cnt++; 
            }
            map[tmp.r][tmp.c] += cnt;
        }
    }
    public static void makeCloud() {
        for(int i = 1; i <= N; i++) {
            for(int j = 1; j <= N; j++) {
                if(!rain[i][j] && map[i][j] >= 2) {
                    cloud.add(new Cloud(i, j));
                    map[i][j] -= 2;
                }
            }
        }
    }
    public static void solution() {
        for(int i = 0; i < M; i++) {
            moveCloudnRain(command[i][0], command[i][1]);
            copywater();
            makeCloud();
        }
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        map = new int[N + 1][N + 1];
        command = new int[M][2];

        cloud = new LinkedList<Cloud>();

        cloud.add(new Cloud(N, 1));
        cloud.add(new Cloud(N, 2));
        cloud.add(new Cloud(N - 1, 1));
        cloud.add(new Cloud(N - 1, 2));
        
        for(int i = 1; i <= N; i++) {
            if(!st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            for(int j = 1; j <= N; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        for(int i = 0; i < M; i++) {
            if(!st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            command[i][0] = Integer.parseInt(st.nextToken());
            command[i][1] = Integer.parseInt(st.nextToken());
        }

        solution();

        int result = 0;
        for(int i = 1; i <= N; i++) {
            for(int j = 1; j <= N; j++) {
                result += map[i][j];
            }
        }

        System.out.println(result);
    }
}

class Cloud {
    int r, c;   // 위치 정보 초기 (N, 1), (N, 2), (N-1, 1), (N-1, 2)
    public Cloud(int r, int c) {
        this.r = r;
        this.c = c;
    }
}
```
