NullNull

[백준 P20056] 마법사 상어와 파이어볼 Java 본문

알고리즘

[백준 P20056] 마법사 상어와 파이어볼 Java

KYBee 2022. 9. 6. 19:12

P20056 마법사 상어와 파이어볼

 

20056번: 마법사 상어와 파이어볼

첫째 줄에 N, M, K가 주어진다. 둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다. 서로 다른 두 파이어볼의 위치

www.acmicpc.net

 

문제

어른 상어가 마법사가 되었고, 파이어볼을 배웠다.

마법사 상어가 크기가 N×N인 격자에 파이어볼 M개를 발사했다. 가장 처음에 파이어볼은 각자 위치에서 이동을 대기하고 있다. i번 파이어볼의 위치는 (ri, ci), 질량은 mi이고, 방향은 di, 속력은 si이다. 위치 (r, c)는 r행 c열을 의미한다.

격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.

파이어볼의 방향은 어떤 칸과 인접한 8개의 칸의 방향을 의미하며, 정수로는 다음과 같다.

 

마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.

  1. 모든 파이어볼이 자신의 방향 d로 속력 s칸 만큼 이동한다.
    • 이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
  2. 이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
    1. 같은 칸에 있는 파이어볼은 모두 하나로 합쳐진다.
    2. 파이어볼은 4개의 파이어볼로 나누어진다.
    3. 나누어진 파이어볼의 질량, 속력, 방향은 다음과 같다.
      1. 질량은 ⌊(합쳐진 파이어볼 질량의 합)/5⌋이다.
      2. 속력은 ⌊(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)⌋이다.
      3. 합쳐지는 파이어볼의 방향이 모두 홀수이거나 모두 짝수이면, 방향은 0, 2, 4, 6이 되고, 그렇지 않으면 1, 3, 5, 7이 된다.
    4. 질량이 0인 파이어볼은 소멸되어 없어진다.

마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 구해보자.

 

 

입력값

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

둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다.

서로 다른 두 파이어볼의 위치가 같은 경우는 입력으로 주어지지 않는다.

  • 4 ≤ N ≤ 50
  • 0 ≤ M ≤ N^2
  • 1 ≤ K ≤ 1,000
  • 1 ≤ r, c ≤ N
  • 1 ≤ m ≤ 1,000
  • 1 ≤ s ≤ 1,000
  • 0 ≤ d ≤ 7
4 2 1
1 1 5 2 2
1 4 7 1 6
4 2 2
1 1 5 2 2
1 4 7 1 6
4 2 3
1 1 5 2 2
1 4 7 1 6
7 5 3
1 3 5 2 4
2 3 5 2 6
5 2 9 1 7
6 2 1 3 5
4 4 2 4 2

 

 

출력값

마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 출력한다.

8
8
0
9

 

 

알고리즘

전형적인 구현 문제이다. 구현 문제를 풀 때는 문제를 풀기 전에 해야하는 일들을 리스팅하고 풀이를 시작한다. 해당 문제를 풀기 위해서 해야 하는 일들은 다음과 같다.

  1. 파이어볼이 이동한다.
  2. 이동이 끝난 뒤에 같은 위치에 있는 파이어볼이 있다면 하나로 합치고 4개의 작은 파이어볼로 나눈다.
  3. 1, 2를 K번 반복한다.
  4. 남아있는 파이어볼의 질량을 더한다.

우선 파이어볼의 위치와 질량, 속력, 방향을 저장하기 위해서 파이어볼 클래스를 선언한다. 그리고 맵에 존재하는 파이어볼을 큐 자료구조에 저장하여 관리하였다.

class Fireball {
    int r, c, m, s, d;

    Fireball(int r, int c, int m, int s, int d) {
        this.r = r;
        this.c = c;
        this.m = m;
        this.s = s;
        this.d = d;
    }
}

// 현재 Map에 있는 모든 파이어볼을 큐에 저장한다.
Queue<Fireball> q = new ArrayDeque<>();
for (int r = 0; r < N; r++) {
    for (int c = 0; c < N; c++) {
        if (!map[r][c].isEmpty()) {
            while (!map[r][c].isEmpty()) {
                q.add(map[r][c].poll());
            }
        }
    }
}

 

현재 맵에 있는 모든 파이어볼은 큐에 들어가 있다. 그래서 큐에 있는 파이어볼을 하나씩 poll 하면서 방향과 속력에 맞게 이동시킨다. 이때 속력 값에 따라 column 인덱스가 오버되는 상황이 발생할 수 있기 때문에 적절한 값을 더하고 모듈러 연산을 취해서 인덱스를 유지시켜야 한다.

while (!q.isEmpty()) {
    Fireball current = q.poll();
    int newR = (current.s * N + current.r + current.s * dr[current.d]) % N;
    int newC = (current.s * N + current.c + current.s * dc[current.d]) % N;

    current.r = newR;
    current.c = newC;

    map[current.r][current.c].add(current);
}

 

이동이 끝난 이후에 특정 위치에 2개 이상 파이어볼이 있는 경우 안에 있는 파이어볼을 합치고 4개의 파이어볼로 나누어야 한다. 이때 신경써야 할 것은 총 질량 / 5를 통해 나누어지는 파이어볼의 질량을 기록해야 하고, 4개의 파이어볼의 방향을 기록해야 한다는 점이다.

우선 모든 파이어볼을 다 더하고 5로 나누었을 때 몫이 0이여서 4개의 파이어볼이 0의 질량을 가지게 된다면 이 경우는 파이어볼을 추가하지 않는다.

 

4개의 파이어볼이 1 이상의 질량을 가진다면 합쳐지기 전 파이어볼들의 방향이 같았는지 확인한다. 만약 다른 방향을 하나라도 발견한다면 dir 플래그 변수를 수정한다.

결과 값을 확인하고 올바르게 파이어볼을 나누어준다.

// map 안의 내용 비교
for (int r = 0; r < N; r++) {
    for (int c = 0; c < N; c++) {
        if (map[r][c].size() > 1) {
            // 내용을 다 바꿔줌
            int size = map[r][c].size();

            int totalM = 0;
            int totalS = 0;

            boolean dir = true;
            boolean isEven = map[r][c].peek().d % 2 == 0;

            while (!map[r][c].isEmpty()){
                Fireball cur = map[r][c].poll();
                totalM += cur.m;
                totalS += cur.s;
                if (dir && isEven != (cur.d % 2 == 0)) dir = false;
            }

            totalM /= 5;
            totalS /= size;

            if (totalM == 0) continue;
            else {
                int cnt = 0;
                if (!dir) cnt = 1;

                for (int k = 0; k < 4; k++) {
                    map[r][c].add(new Fireball(r, c, totalM, totalS, cnt));
                    cnt += 2;
                }
            }
        }
    }
}

 

이를 K 번 반복한 이후, 맵에 남아있는 파이어볼의 질량들을 다 더해서 결과를 도출한다.

int total = 0;

for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
        if (!map[i][j].isEmpty()) {
            while(!map[i][j].isEmpty()) {
                total += map[i][j].poll().m;
            }
        }
    }
}

System.out.println(total);

 

코드

import java.util.*;
import java.io.*;

public class Main {
    static final int[] dr = {-1, -1, 0, 1, 1, 1, 0, -1};
    static final int[] dc = {0, 1, 1, 1, 0, -1, -1, -1};

    static int N, M, K;
    static Queue<Fireball>[][] map;

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

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

        map = new Queue[N][N];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                map[i][j] = new ArrayDeque<>();
            }
        }

        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int r = Integer.parseInt(st.nextToken()) - 1;
            int c = Integer.parseInt(st.nextToken()) - 1;
            int m = Integer.parseInt(st.nextToken());
            int s = Integer.parseInt(st.nextToken());
            int d = Integer.parseInt(st.nextToken());

            map[r][c].add(new Fireball(r, c, m, s, d));
        }

        for (int i = 0; i < K; i++) {
            Queue<Fireball> q = new ArrayDeque<>();

            for (int r = 0; r < N; r++) {
                for (int c = 0; c < N; c++) {
                    if (!map[r][c].isEmpty()) {
                        while (!map[r][c].isEmpty()) {
                            q.add(map[r][c].poll());
                        }
                    }
                }
            }

            while (!q.isEmpty()) {
                Fireball current = q.poll();
                int newR = (current.s * N + current.r + current.s * dr[current.d]) % N;
                int newC = (current.s * N + current.c + current.s * dc[current.d]) % N;

                current.r = newR;
                current.c = newC;

                map[current.r][current.c].add(current);
            }

            // map 안의 내용 비교
            for (int r = 0; r < N; r++) {
                for (int c = 0; c < N; c++) {
                    if (map[r][c].size() > 1) {
                        // 내용을 다 바꿔줌
                        int size = map[r][c].size();

                        int totalM = 0;
                        int totalS = 0;

                        boolean dir = true;
                        boolean isEven = map[r][c].peek().d % 2 == 0;

                        while (!map[r][c].isEmpty()){
                            Fireball cur = map[r][c].poll();
                            totalM += cur.m;
                            totalS += cur.s;
                            if (dir && isEven != (cur.d % 2 == 0)) dir = false;
                        }

                        totalM /= 5;
                        totalS /= size;

                        if (totalM == 0) continue;
                        else {
                            int cnt = 0;
                            if (!dir) cnt = 1;

                            for (int k = 0; k < 4; k++) {
                                map[r][c].add(new Fireball(r, c, totalM, totalS, cnt));
                                cnt += 2;
                            }
                        }
                    }
                }
            }
        }

        int total = 0;

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (!map[i][j].isEmpty()) {
                    while(!map[i][j].isEmpty()) {
                        total += map[i][j].poll().m;
                    }
                }
            }
        }

        System.out.println(total);
    }
}

class Fireball {
    int r, c, m, s, d;

    Fireball(int r, int c, int m, int s, int d) {
        this.r = r;
        this.c = c;
        this.m = m;
        this.s = s;
        this.d = d;
    }

    @Override
    public String toString() {
        return r + " " + c + " : " + m;
    }
}
Comments