접근 방법
시뮬레이션 문제이기 때문에 문제에서 어떤 데이터가 필요한지 먼저 분석했다.
파이어볼이 이동한 후 위치에 대한 파이어볼 정보를 파악해야 했다 .
→ map[][]에 파이어볼의 정보를 저장
한 위치에 여러 개의 파이어볼이 존재할 수 있다.
→ map[][]을 리스트로 보관해야 할까? (하나로 합쳐서 저장할 수 있다)
파이어볼이 나누어지는 과정의 단순화
- 질량 : 파이어볼들의 질량 합에서 상수인 5로 나누기 때문에 누적해서 저장하면 된다.
- 속력 : 파이어볼들의 속력 합에서 개수로 나누기 때문에 누적해서 저장하고 개수를 따로 세어주면 된다.
- 방향 : 모두 홀수이거나 모두 짝수인 경우를 생각해야 하는데 Boolean 2개로 처리하자.
따라서 파이어볼 정보(m, s, d, cnt, odd, even)를 담는 Ball 클래스를 선언하였다.
m, s, d는 입력값이며 cnt는 몇 개의 파이어볼이 합쳐졌는지를 저장한다. odd와 even은 홀수나 짝수가 있다면 true로 만들어서 둘 중 하나라도 false가 있다면 모두 홀수이거나 모두 짝수인 경우라고 판단하였다.
Ball map[][]에는 해당 위치에 존재하는 파이어볼의 정보를 저장하였다.
파이어볼이 나누어질 때 d는 모두 짝수이거나 모두 홀수라면 0으로 설정하고
아니라면 1로 설정한 후 d부터 +2를 한 방향으로 파이어볼을 이동시켰다.
삽질했던 부분
1. 파이어 볼 이동의 동시성
이미 이동한 파이어볼과 이동할 파이어볼을 따로 생각해야 했다.
파이어볼을 하나로 합치는 방식으로 구현했기 때문에 A 파이어볼을 B 파이어볼 위치로 이동시키는데 B 파이어볼이 아직 이동하기 전이라면 논리적 오류가 발생한다.
따라서 임시맵을 만들어서 단계를 나누어 주었다.
2. 짝수/홀수 판단의 초기화
Boolean 2개로 모두 짝수인지 모두 홀수인지 검사하도록 만들었는데
초기 방향에 대해서는 체크를 해주지 않아서 디버깅을 꽤 오래 했다... 8ㅅ8
간단하게 생성자에 추가하여 구현하였다.
후기
삼성 역량 테스트 오전 문제였는데 삽질을 많이 해서인지 내가 본 오후 시험보다 어렵게 느껴졌다.
구현력을 키우기 위한 좋은 문제라고 생각했다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
/*
* 20056. 마법사 상어와 파이어볼
* https://www.acmicpc.net/problem/20056
*/
public class Main {
static int dr[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
static int dc[] = { 0, 1, 1, 1, 0, -1, -1, -1 };
static Ball map[][], next[][];
static int ans, N;
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());
int M = Integer.parseInt(st.nextToken());
int K = Integer.parseInt(st.nextToken());
map = new Ball[N][N];
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] = new Ball(m, s, d);
}
for (int i = 0; i < K; i++)
move();
sol();
System.out.println(ans);
}
static void move() {
next = new Ball[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (map[i][j] == null)
continue;
// 해당 위치에 파이어볼이 존재한다면 정보를 확인한다.
int move = 1; // 이동할 파이어볼의 개수
Ball b = map[i][j];
// 여러개의 파이어 볼이 있다면 조건에 맞게 하나로 만들고 move만 4로 해준다.
if (b.cnt > 1) {
b.d = (!b.even || !b.odd) ? 0 : 1;
b.m /= 5;
b.s /= b.cnt;
move = 4;
if (b.m == 0)
continue;
}
// 파이어볼의 방향으로 이동시켜준다.
// 이때 move가 4라면 d부터 돌면서 4번 이동시킨다.
for (int k = 0; k < move; k++, b.d += 2) {
int nr = (i + dr[b.d] * b.s + (N * b.s)) % N;
int nc = (j + dc[b.d] * b.s + (N * b.s)) % N;
// 새로운 위치에 파이어볼이 없다면 그냥 대입하고
if (next[nr][nc] == null)
next[nr][nc] = new Ball(b.m, b.s, b.d);
// 새로운 위치에 파이어볼이 존재한다면 합쳐준다.
else {
Ball b2 = next[nr][nc];
b2.m += b.m;
b2.s += b.s;
b2.cnt++;
if (b.d % 2 == 0)
b2.even = true;
else
b2.odd = true;
}
}
}
}
for (int i = 0; i < N; i++)
map[i] = next[i].clone();
}
// 남은 파이어볼들의 질량을 더해준다.
static void sol() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
Ball b = map[i][j];
if (b == null)
continue;
if (b.cnt == 1)
ans += b.m;
else // 원래는 이동이 끝나면 바로 나누어지기 때문에 나눈 후의 질량을 더해준다.
ans += b.m / 5 * 4;
}
}
}
static class Ball {
int m, s, d, cnt;
boolean odd, even;
public Ball(int m, int s, int d) {
this.m = m;
this.s = s;
this.d = d;
this.cnt = 1;
if (this.d % 2 == 0)
even = true;
else
odd = true;
}
}
}
'BOJ' 카테고리의 다른 글
백준 13398 : 연속합 2 (1) | 2020.12.13 |
---|---|
백준 20159 : 동작 그만. 밑장 빼기냐? (0) | 2020.12.10 |
백준 2589 : 보물섬 (0) | 2020.10.17 |
백준 19236 : 청소년 상어 (0) | 2020.10.15 |