본문 바로가기

코딩 테스트/BAEKJOON

[Java] 20056 마법사 상어와 파이어볼

문제

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

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

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

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

7   0  1
6      2
5   4  3
마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.

모든 파이어볼이 자신의 방향 di로 속력 si칸 만큼 이동한다.
이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
같은 칸에 있는 파이어볼은 모두 하나로 합쳐진다.
파이어볼은 4개의 파이어볼로 나누어진다.
나누어진 파이어볼의 질량, 속력, 방향은 다음과 같다.
질량은 ⌊(합쳐진 파이어볼 질량의 합)/5⌋이다.
속력은 ⌊(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)⌋이다.

합쳐지는 파이어볼의 방향이 모두 홀수이거나 모두 짝수이면, 
방향은 0, 2, 4, 6이 되고, 그렇지 않으면 1, 3, 5, 7이 된다.

질량이 0인 파이어볼은 소멸되어 없어진다.
마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 구해보자.

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

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

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

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

문제 풀이

배열의 각 위치별로 파이어볼의 정보를 저장할 리스트를 두고 이동하는 메서드, 각 위치의 파이어볼을 계산해서 분열하는 메소드를 두어서 위치와 개수를 계산한다. 자세한 로직은 주석으로 처리한다.

자바 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class N20056 {
    private static ArrayList<FireBall>[][] map;
    // 현재 살아있는 파이어볼 리스트
    private static final ArrayList<FireBall> fireBalls = new ArrayList<>();
    private static final int[] dr = {-1, -1, 0, 1, 1, 1, 0, -1};	//방향 r값 변경값
    private static final int[] dc = {0, 1, 1, 1, 0, -1, -1, -1};	//방향 c값 변경값

    static class FireBall{
        // x,y 좌표, 질량, 속도, 방향
        private int r,c,m,s,d;
        public 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;
        }

        public void setR(int r) {
            this.r = r;
        }

        public void setC(int c) {
            this.c = c;
        }

        public int getR() {
            return r;
        }

        public int getC() {
            return c;
        }

        public int getM() {
            return m;
        }

        public int getS() {
            return s;
        }

        public int getD() {
            return d;
        }

        public void setM(int m) {
            this.m = m;
        }

        public void setS(int s) {
            this.s = s;
        }

        public void setD(int d) {
            this.d = d;
        }
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        int n = Integer.parseInt(st.nextToken());
        int m1 = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());

        // 각 위치별 파이어볼 정보리스트
        map = new ArrayList[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++)
                map[i][j] = new ArrayList<>();
        }

        for(int i = 0; i < m1; i++){
            st = new StringTokenizer(br.readLine(), " ");
            int r = Integer.parseInt(st.nextToken());
            int c = Integer.parseInt(st.nextToken());
            int m = Integer.parseInt(st.nextToken());
            int s = Integer.parseInt(st.nextToken());
            int d = Integer.parseInt(st.nextToken());
            fireBalls.add(new FireBall(r, c, m, s, d));
        }

        for(int i = 0; i < k; i++){
            Move(n);
            SumAndDivide(n);
        }

        FireBallMetricSum();
    }

    // 현재 살아있는 파이어볼 위치이동 메소드
    public static void Move(int N){
        for(FireBall fb : fireBalls){
            // (현재 위치 + N + 이동 방향 * (속도 % N)) % N
            // 속도에 % N 연산을 하는 이유는 속도가 N을 넘어가도 사실상 이동하는 거리는 N을 벗어나지 않기 때문
            // + N을 해주는 이유는 이동 방향 연산을 통해서 값이 음수가 될 수 있기 때문
            int tempR = (fb.getR() + N + dr[fb.getD()] * (fb.getS() % N)) % N;
            int tempC = (fb.getC() + N + dc[fb.getD()] * (fb.getS() % N)) % N;
            
            // 연산한 위치로 파이어볼 이동
            fb.setR(tempR);
            fb.setC(tempC);

            // 이동한 위치에 해당 파이어볼 정보를 저장
            map[fb.getR()][fb.getC()].add(fb);
        }
    }

    public static void SumAndDivide(int N){
        for(int i = 0; i < N; i++){
            for(int j = 0; j < N; j++){
                // 위치에 파이어볼이 2개 미만 일 경우 그냥 무시
                if(map[i][j].size() < 2){
                    map[i][j].clear();
                    continue;
                }

                // M과 S의 총합 변수, 방향이 홀/짝수인 메테오의 숫자 변수, 해당 위치의 파이어볼 수
                int sumM = 0, sumS = 0, oddCount = 0, evenCount = 0;
                int size = map[i][j].size();

                // 해당 위치의 파이어볼 정보를 하나씩 찾는다.
                for(FireBall fb : map[i][j]){
                    // M과 S의 값을 더해준다.
                    sumM += fb.getM();
                    sumS += fb.getS();

                    // 방향이 홀수인지 짝수인지 검사
                    if(fb.getD() % 2 == 1)
                        oddCount++;
                    else
                        evenCount++;

                    // 값을 처리하여(합쳐진) 파이어볼 제거
                    fireBalls.remove(fb);
                }

                // 해당 위치의 파이어볼 모두 제거
                map[i][j].clear();
                // 새로 생성된 파이어볼들의 질량 계산, 0이면 생성하지 않는다.
                sumM /= 5;
                if(sumM == 0)
                    continue;

                // 새로 생성된 파이어볼들의 속도 계산
                // 전부 홀수거나 짝수이면 0,2,4,6 방향의 메테오
                // 아니면 1,3,5,7 방향의 메테오를 생성하여 추가한다.
                sumS /= size;
                if(oddCount == size || evenCount == size)
                    for(int d = 0; d < 8; d += 2)
                        fireBalls.add(new FireBall(i, j, sumM, sumS, d));
                else
                    for(int d = 1; d < 8; d += 2)
                        fireBalls.add(new FireBall(i, j, sumM, sumS, d));
            }
        }
    }

    public static void FireBallMetricSum(){
        int result = 0;
        // 각 파이어볼들의 질량을 더한다.
        for(FireBall fb : fireBalls){
            result += fb.getM();
        }

        System.out.println(result);
    }
}

 

'코딩 테스트 > BAEKJOON' 카테고리의 다른 글

[Java] 11727 2xn 타일링 2  (0) 2024.05.20
[Java] 1959 달팽이3  (0) 2024.05.15
[Java] 15558 점프 게임  (0) 2024.05.14
[Java] 16927 배열 돌리기 2  (0) 2024.05.09
[Java] 16926 배열돌리기 1  (0) 2024.05.09