www.acmicpc.net/problem/20056

 

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개의 칸의 방향을 의미하며, 정수로는 다음과 같다.

7 0 1
6   2
5 4 3

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

  1. 모든 파이어볼이 자신의 방향 di로 속력 si칸 만큼 이동한다.
    • 이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
  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로 이루어져 있다.

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

출력

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

제한

  • 4 ≤ N ≤ 50
  • 0 ≤ M ≤ N2
  • 1 ≤ K ≤ 1,000
  • 1 ≤ ri, ci ≤ N
  • 1 ≤ mi ≤ 1,000
  • 1 ≤ si ≤ 1,000
  • 0 ≤ di ≤ 7

예제 입력 1

4 2 1

1 1 5 2 2

1 4 7 1 6

예제 출력 1

8

예제 입력 2

4 2 2

1 1 5 2 2

1 4 7 1 6

예제 출력 2

8

예제 입력 3

4 2 3

1 1 5 2 2

1 4 7 1 6

예제 출력 3

0

예제 입력 4

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

예제 출력 4

9

 

 

 

 

 

 

 

풀이 .

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

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

public class Main {
    static int n, m, k, ans;
    static boolean turnFlag;
    static ArrayList<Fireball>[][] map;
    static int[] rArr = {-1, -1, 0, 1, 1, 1, 0, -1};
    static int[] cArr = {0, 1, 1, 1, 0, -1, -1, -1};

    public static void calculate() {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                if(map[i][j].isEmpty()) continue;
                for(Fireball f : map[i][j]) {
                    ans += f.m;
                }
            }
        }
    }

    public static void moveBall() {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                if(map[i][j].isEmpty()) continue;

                for(int idx = 0; idx < map[i][j].size(); idx++) {
                    Fireball f = map[i][j].get(idx);
                    if(f.flag == turnFlag) continue;  // 이번 턴에 이동해서 온 것은 또 이동시키면 안 딤

                    int nr = f.r + rArr[f.d] * (f.s % n);
                    int nc = f.c + cArr[f.d] * (f.s % n);
                    if(nr > 0) nr %= n;
                    if(nc > 0) nc %= n;
                    if(nr < 0) nr = n - Math.abs(nr);
                    if(nc < 0) nc = n - Math.abs(nc);
                    // (f.s % n)을 곱하기 때문에 n-Math.abs()는 반드시 양수 보장

                    f.r = nr;
                    f.c = nc;
                    f.flag = turnFlag;
                    map[nr][nc].add(f);
                    map[i][j].remove(idx--);
                }
            }
        }
    }

    public static void sumAndDivide() {
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                int size = map[i][j].size();
                if(size < 2) continue;

                // sum
                int mSum = 0, sSum = 0;
                int oddCnt = 0, evenCnt = 0;
                for(Fireball f : map[i][j]) {
                    mSum += f.m;
                    sSum += f.s;
                    if(f.d % 2 != 0) oddCnt += 1;
                    if(f.d % 2 == 0) evenCnt += 1;
                }
                map[i][j].clear();  // 합쳐진 파이어볼들은 삭제해준다

                // divide
                int nm = mSum/5, ns = sSum/size, d = 1;
                if(nm == 0) continue;  // 질량이 0인 파이어볼은 사라진다
                if(oddCnt == size || evenCnt == size)
                    d = 0;  // 모두 홀수 or 모두 짝수이면 0,2,4,6
                for(int nd = d; nd <= 7; nd+=2) {
                    Fireball f = new Fireball(i, j, nm, ns, nd, turnFlag);
                    map[i][j].add(f);
                }
            }
        }
    }

    public static void process() {
        while(k-- > 0) {
            moveBall();
            sumAndDivide();
            turnFlag = !turnFlag;
        }
        calculate();
    }

    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());
        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 < 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());
            Fireball f = new Fireball(r, c, m, s, d, true);
            map[r][c].add(f);
        }

        process();
        System.out.println(ans);
    }
}

 

이틀동안 해맸다.

 

s를 처음 입력받을 때부터 % n해서 입력받았는데 그게 문제였다. 처음부터 속도를 % 으로 받아버리면 파이어볼이 합쳐진 후에 나눠지는 과정에서 ns가 제대로 된 값을 얻지 못한다.

 

n == 4에서 s == 7, s == 1 일 경우 8/2 로 ns == 4가 되어야 하는데..

%으로 받으면 3, 1로 들어가기 때문에 ns == 2가 되는 문제였다.

 

 

근데 위 코드보다는 아래 방법이 더 나은 듯 하다.

 

1. Fireball을 담는 1차원 list 하나를 따로 둔다.

2. map[][]에는 int[]를 담고, 거기에는 "1."의 리스트의 인덱스를 담는다.

3. moveBall에서는 newMap[][]을 만들어 이동한 위치에 인덱스를 담은 후 map = newMap으로 덮어씌운다.

4. sumAndDivide에서는 newList를 만들어서 처리 후 덮어씌운다.

 

 

 

 

+ Recent posts