문제
어른 상어가 마법사가 되었고, 파이어볼을 배웠다.
마법사 상어가 크기가 N×N인 격자에 파이어볼 M개를 발사했다. 가장 처음에 파이어볼은 각자 위치에서 이동을 대기하고 있다. i번 파이어볼의 위치는 (ri, ci), 질량은 mi이고, 방향은 di, 속력은 si이다. 위치 (r, c)는 r행 c열을 의미한다.
격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.
파이어볼의 방향은 어떤 칸과 인접한 8개의 칸의 방향을 의미하며, 정수로는 다음과 같다.
마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.

- 모든 파이어볼이 자신의 방향 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번 명령한 후, 남아있는 파이어볼 질량의 합을 출력한다.
제한
- 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
풀이
import java.io.*;
import java.util.*;
public class Main {
static final int[] dx = {-1, -1, 0, 1, 1, 1, 0, -1};
static final int[] dy = {0, 1, 1, 1, 0, -1, -1, -1};
static final int[] o = {0, 2, 4, 6};
static final int[] x = {1, 3, 5, 7};
static BufferedReader br;
static BufferedWriter bw;
static StringTokenizer st;
static ArrayList<FireBall> list;
static ArrayList<FireBall>[][] fireBalls;
static int n, m, k, tmp = 0;
public static void main(String[] args) throws IOException {
br = new BufferedReader(new InputStreamReader(System.in));
bw = new BufferedWriter(new OutputStreamWriter(System.out));
//input
st = new StringTokenizer(br.readLine());
n = stoi(st.nextToken());
m = stoi(st.nextToken());
k = stoi(st.nextToken());
fireBalls = new ArrayList[n][n];
list = new ArrayList<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
fireBalls[i][j] = new ArrayList<>();
}
}
//초기 파이어볼 지정
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
int r = stoi(st.nextToken()) - 1;
int c = stoi(st.nextToken()) - 1;
int m = stoi(st.nextToken());
int s = stoi(st.nextToken());
int d = stoi(st.nextToken());
list.add(new FireBall(r, c, m, s, d));
tmp += m;
}
//logic
while(k-- > 0) {
move();
divide();
}
//output
bw.write(tmp + "\n");
bw.flush();
bw.close();
}
static int stoi(String s) {return Integer.parseInt(s);}
static void move() {
for(FireBall cur : list) {
//좌표 모듈러 연산
int nr = ((cur.s % n) * dx[cur.d] + n + cur.r) % n;
int nc = ((cur.s % n) * dy[cur.d] + n + cur.c) % n;
cur.r = nr;
cur.c = nc;
fireBalls[nr][nc].add(cur);
}
}
static void divide() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
//2개 이상의 파이어볼이 있을 경우
if(fireBalls[i][j].size() == 1) {
fireBalls[i][j].clear();
}
if(fireBalls[i][j].size() < 2) {
continue;
}
//파이어볼 하나로 합치기
int mSum = 0;
int sSum = 0;
int even = 0;
int odd = 0;
for(FireBall cur : fireBalls[i][j]) {
sSum += cur.s;
mSum += cur.m;
if(cur.d % 2 == 0) {
even++;
} else {
odd++;
}
//파이어볼을 합쳐야 하니 리스트에서 하나씩 삭제해준다.
list.remove(cur);
}
tmp -= mSum;
int newM = mSum / 5;
int newS = sSum / fireBalls[i][j].size();
fireBalls[i][j].clear();
//파이어볼을 나눈 후 질량이 0이라면, 파이어볼 소멸
if(newM < 1) {
continue;
}
//파이어볼 4개로 나누기
for (int r = 0; r < 4; r++) {
int newD = odd == 0 || even == 0 ? o[r] : x[r];
list.add(new FireBall(i, j, newM ,newS, newD));
tmp += newM;
}
}
}
}
}
class FireBall {
int r;
int c;
int m;
int s;
int 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;
}
}
조건에 맞는 정확한 구현을 요하는 문제 였습니다.
크게 2단계로 나눠집니다.
1. 파이어볼의 이동 = move()
2. 한 칸에 파이어볼이 2개 이상인 곳의 파이어볼을 합치고 나누기 = devide()
1번 같은 경우는 모듈러 연산을 통해서 반복 연산의 횟수를 줄여줍니다.
좌표 연산을 통해 list에 있는 파이어볼의 좌표를 바꾸어주고 해당 좌표의 리스트에 넣습니다. 추후 devide() 시에 해당 좌표에 있는 파이어볼을 알기 위함 입니다.
2번은 해당 좌표에 있는 파이어볼이 2개 미만일 시 해당 좌표의 리스트를 초기화 해줍니다. 어처피 파이어볼의 관리는 list에서 하고있기 때문에 초기화 해도 문제가 없습니다.
2개 이상일 때는 해당 좌표에 있는 파이어볼들의 질량, 속력의 총합을 구해줍니다. 이 때 각 파이어볼들이 짝수인지 홀수인지도 세줍니다. 그리고 하나의 파이어볼로 합친다고 했으니 list에 있는 파이어볼을 remove 해줍니다.
모두 구했다면 역시 해당 좌표의 리스트를 초기화 해줍니다. (위와 같은 이유)
만약 질량의 총합 / 5 가 0이면 해당 좌표에 있는 파이어볼들은 사라집니다.
질량의 총합 / 5 가 1 이상이라면 파이어볼을 4개로 나누어 줍니다. 전에 각 파이어볼의 홀수, 짝수의 개수를 세었기 때문에 이를 토대로 조건에 맞게 새로운 파이어볼을 list에 생성해줍니다.
이를 k번 반복해주면 알맞는 정답이 나옵니다.
'Dev > PS' 카테고리의 다른 글
[백준] 2206 벽 부수고 이동하기 java (0) | 2023.02.14 |
---|---|
[BOJ] 1074 Z java (0) | 2023.02.07 |
[백준] 17144 미세먼지 안녕! java (0) | 2022.08.18 |
[백준] 21610 마법사 상어와 비바라기 java (0) | 2022.08.11 |
[백준] 16234 인구 이동 java (0) | 2022.08.10 |