BOJ 20056: 마법사 상어와 파이어볼 https://www.acmicpc.net/problem/20056
파이어볼의 위치는 (r, c)
, 질량은 m
이고, 방향은 d
, 속력은 s
이다. 위치 (r, c)는 r행 c열을 의미한다.
격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.
범위를 넘어가면 그 반대쪽 위치로 이동
한다는 뜻이다.파이어볼의 방향
은 어떤 칸과 인접한 8개의 칸의 방향을 의미하며, 정수로는 다음과 같다.
마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.
4개
의 파이어볼로 나누어진다.(합쳐진 파이어볼 질량의 합)/5
이다.(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)
이다.방향이 모두 홀수이거나 모두 짝수이면, 방향은 0, 2, 4, 6
이 되고, 그렇지 않으면 1, 3, 5, 7
이 된다.파이어볼의 현재 위치 등 정보
를 담을 Queue 배열(que)
을 생성 및 초기화한다.파이어볼의 새로운 위치
를 기록해놓을 Queue 배열(extQue)
을 생성 및 초기화한다.extQue
에 이동이 완료된 좌표와 객체
를 넣는다.que
에 extQue 값을 복사
한다.import java.util.*;
import java.io.*;
public class Main {
static int N, M, K;
static int[][] info;
static Queue<Fire>[][] que;
static int[] dx = {-1, -1, 0, 1, 1, 1, 0, -1}; // 방향
static int[] dy = {0, 1, 1, 1, 0, -1, -1, -1};
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()); // 이동 명령 횟수
info = new int[M][5]; // 행, 열, 질량, 속력, 방향
// Queue 배열 생성 및 초기화
que = new LinkedList[N+1][N+1];
for(int i=0; i<=N; i++) {
for(int j=0; j<=N; j++) {
que[i][j] = new LinkedList<Fire>();
}
}
// Queue 배열에 입력 받은 파이어볼 객체 삽입
for(int i=0; i<M; i++) {
st = new StringTokenizer(br.readLine(), " ");
for(int j=0; j<5; j++) {
info[i][j] = Integer.parseInt(st.nextToken());
}
Fire f = new Fire(info[i][0], info[i][1], info[i][2], info[i][3], info[i][4]);
que[f.x][f.y].add(f);
}
// 명령 횟수 만큼 반복
for(int k=1; k<=K; k++) {
// 각 파이어볼이 이동함
move();
// 위치에 있는 파이어볼들을 합치고 4개로 나눔
combine();
}
// 답 출력
int ans = 0;
for(int i=1; i<=N; i++) {
for(int j=1; j<=N; j++) {
if(!que[i][j].isEmpty()) {
while(!que[i][j].isEmpty()) {
Fire f = que[i][j].poll();
ans += f.m;
}
}
}
}
System.out.println(ans);
}
// 위치에 있는 파이어볼들을 합치고 4개로 나눌 메소드
static void combine() {
for(int i=1; i<=N; i++) {
for(int j=1; j<=N; j++) {
// 해당 위치에 파이어볼이 2개 이상 있다면
if(que[i][j].size() >= 2) {
int mSum = 0; // 질량의 합
int sSum = 0; // 속력의 합
int queSize = que[i][j].size(); // 파이어볼의 갯수
ArrayList<Boolean> even = new ArrayList<>(); // 짝수 방향을 넣을 리스트
ArrayList<Boolean> odd = new ArrayList<>(); // 홀수 방향을 넣을 리스트
while(!que[i][j].isEmpty()) {
Fire f = que[i][j].poll();
mSum += f.m; // 질량을 더함
sSum += f.s; // 속력을 더함
// 방향의 홀, 짝 을 구분해서 리스트에 넣어줌
if(f.d % 2 == 1) {
odd.add(true);
} else {
even.add(true);
}
}
int nM = mSum / 5; // 새로운 질량
int nS = sSum / queSize; // 새로운 속력
// 새로운 질량이 0이면 넘어감
if(nM == 0) {
continue;
}
// 방향이 모두 짝수이거나 홀수라면
if(even.size() == 0 || odd.size() == 0) {
// 방향: 0, 2, 4, 6
que[i][j].add(new Fire(i, j, nM, nS, 0));
que[i][j].add(new Fire(i, j, nM, nS, 2));
que[i][j].add(new Fire(i, j, nM, nS, 4));
que[i][j].add(new Fire(i, j, nM, nS, 6));
} else {
// 방향: 1, 3, 5, 7
que[i][j].add(new Fire(i, j, nM, nS, 1));
que[i][j].add(new Fire(i, j, nM, nS, 3));
que[i][j].add(new Fire(i, j, nM, nS, 5));
que[i][j].add(new Fire(i, j, nM, nS, 7));
}
}
}
}
}
// 파이어볼 이동시키는 메소드
static void move() {
// 파이어볼의 새로운 위치를 기록해놓을 Queue 배열 : extQue
Queue<Fire>[][] extQue = new LinkedList[N+1][N+1];
for(int i=0; i<=N; i++) {
for(int j=0; j<=N; j++) {
extQue[i][j] = new LinkedList<Fire>();
}
}
// 각 좌표를 탐색하며 수행
for(int i=1; i<=N; i++) {
for(int j=1; j<=N; j++) {
// 해당 que에 파이어볼이 들어 있다면
if(!que[i][j].isEmpty()) {
while(!que[i][j].isEmpty()) {
Fire f = que[i][j].poll();
int curX = f.x;
int curY = f.y;
// 속력 만큼 이동
for(int s=1; s<=f.s; s++) {
curX += dx[f.d];
curY += dy[f.d];
// 새로운 위치가 범위를 넘어가면 반대편 쪽으로 넘어가게 함
// 문제 조건 잘 볼 것
if(curX <= 0) {
curX = N;
} else if(curX > N) {
curX = 1;
}
if(curY <= 0) {
curY = N;
} else if(curY > N) {
curY = 1;
}
}
Fire nf = new Fire(curX, curY, f.m, f.s, f.d);
// extQue에 새로운 위치로 이동한 파이어볼을 넣음
extQue[curX][curY].add(nf);
}
}
}
}
// 원본 Queue배열인 que에 extQue 값을 복사함
for(int i=1; i<=N; i++) {
for(int j=1; j<=N; j++) {
if(!extQue[i][j].isEmpty()) {
while(!extQue[i][j].isEmpty()) {
Fire f = extQue[i][j].poll();
que[i][j].add(f);
}
}
}
}
}
// 파이어볼 정보를 담을 객체
static class Fire{
int x, y, m, s, d;
Fire(int x, int y, int m, int s, int d){
this.x = x;
this.y = y;
this.m = m;
this.s = s;
this.d = d;
}
}
}
격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.
라는 조건 해석을 못해서 시간 낭비를 했다.