[java] 백준(BOJ) - 마법사 상어와 파이어볼
업데이트:
문제
백준-마법사 상어와 파이어볼
파이어볼이 명령에 따라 K번 이동 한 후 남아있는 파이어볼 질량의 합을 구하는 문제
- 제한 사항
맵의 크기 N : 4 ≤N
≤ 50
초기 파이어볼 개수 M : 0 ≤M
≤ N2
명령의 수 K : 1 ≤K
≤ 1,000
각 파이어볼의 질량 mi : 1 ≤mi
≤ 1,000
각 파이어볼의 속력 si : 1 ≤si
≤ 1,000
각 파이어볼의 방향 di : 0 ≤di
≤ 7
풀이
시뮬 특징 그대로 시키는 대로 하면 된다
다만, N = 3인 맵의 [0, 0] 위치에서 2방향(우측)으로 5칸 움직인다고 하면
y값이 [0, 1, 2, 0, 1] 이렇게 움직여야한다 (행도 마찬가지)
시행착오 : 시간 소모 및 틀렸습니다
어레이리스트와 스택2개를 이용해서 풀이를 해봤다
대략적으로 설명하자면
- 처음 파이어볼을 리스트(balls)에 넣는다
- 리스트(balls)의 원소들을 순회하면서 다음 이동경로로 x,y를 바꿔준다
- 리스트를 정렬한다(x,y 기준으로)
- 리스트를 순환하면서 스택(tmp)와 다른 스택(cnt)에 넣는다
(tmp는 ball을 담고, cnt는 ball의 방향에 따른 홀수, 짝수를 저장함)
4-1. 이때 기존 스택(tmp)의 peek값과 x,y가 같으면 질량과 속도를 더해준다
4-2. 4-1.에 이어, cnt에 현재 방향이 홀수면 홀수 증가, 짝수면 짝수 증가 - tmp와 cnt를 같이 뽑으면서 balls에 추가한다
5-1. cnt의 홀수+cnt의 짝수 > 1이면 2개 이상의 ball이 모인것이므로 tmp에서 뽑은 ball의 질량과 속력을 계산해서 원래 리스트(balls)에 4개를 추가한다
5-2. 아닌경우 그냥 그대로 리스트(balls)에 추가
후.. 틀렸습니다도 초반에 조금 0%에서 떴다
< 내가 생각하는 원인 >
-
다음 위치의 계산을 잘못함
nx, ny를 구할 때 0보다 작으면 N을 더해주고, N보다 크면 N을 뺐다
N = 50 이고 [0,0]위치에서 1000의 속도로 움직이면 [950, 0] 이렇게 나왔다
리스트를 사용하니까 딱히 맵에 제약을 두지않았는데, 디버깅하다가 알았다 -
입력값 미스
나는 0부터 시작한다고 생각하고 코드를 짰다
근데 문제 입력은 1이 인덱스의 시작값이다
ㅠㅠ바보 -
문제 이해 실수
처음에 파이어볼이 4개로 갈라진다는게, 해당 방향의 위치로 나눠진다는 줄 알았다
그런데 지금위치에 4개가 다른방향으로 분열되는 거였다
다음차례의 이동에서 갈라진 파이어볼이 방향따라 제 갈길을 가는거였다
다 수정하고 제출했는데 정답이었지만, 반복해서 사용하는 스택2개 탓에 속도는 오늘시작하면 내일끝날 느낌이었다
그래서 ArrayList<Ball>[][][]
로 수정했다
[x][y][flag]로 해서 몇번째 이동인지에 따라 flag를 교체하면서 사용했다
방법은 그대로 따라하면 되는거라서 생략 ~~
소스코드
(시뮬문제는 문제조건을 복사해서 주석달고 그~대로 써서 소스가 좀 길다)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.StringTokenizer;
public class Main {
private static int N, M, K;
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());
ArrayList<Ball>[][][] balls = new ArrayList[N][N][2];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < 2; k++) {
balls[i][j][k] = new ArrayList<>();
}
}
}
int x, y, m, s, d;
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
x = Integer.parseInt(st.nextToken()) - 1;
y = Integer.parseInt(st.nextToken()) - 1;
m = Integer.parseInt(st.nextToken());
s = Integer.parseInt(st.nextToken());
d = Integer.parseInt(st.nextToken());
balls[x][y][0].add(new Ball(m, s, d));
}
for (int i = 0; i < K; i++) {
// 1.모든 파이어볼이 자신의 방향 di로 속력 si칸 만큼 이동한다.
int flag = i % 2;
int next = flag == 0 ? 1 : 0;
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
while (!balls[r][c][flag].isEmpty()) {
Ball curr = balls[r][c][flag].remove(0);
int nx = r + dx[curr.d] * (curr.s % N);
int ny = c + dy[curr.d] * (curr.s % N);
nx = checkBoundary(nx);
ny = checkBoundary(ny);
balls[nx][ny][next].add(new Ball(curr.m, curr.s, curr.d));
}
}
}
// 이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
// 2.이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
// 2-1.같은 칸에 있는 파이어볼은 모두 하나로 합쳐진다.
// 2-2.파이어볼은 4개의 파이어볼로 나누어진다.
// 2-3-1.질량은 ⌊(합쳐진 파이어볼 질량의 합)/5⌋이다.
// 2-3-2.속력은 ⌊(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)⌋이다.
if (balls[r][c][next].size() > 1) {
int ms = 0;
int ss = 0;
int v[] = new int[2];
while (!balls[r][c][next].isEmpty()) {
Ball curr = balls[r][c][next].remove(0);
if (curr.d % 2 == 0)
v[0]++;
else
v[1]++;
ms += curr.m;
ss += curr.s;
}
ms /= 5;
// 2-4.질량이 0인 파이어볼은 소멸되어 없어진다.
if (ms == 0)
continue;
int size = v[0] + v[1];
ss /= size;
// 2-3-3.합쳐지는 파이어볼의 방향이 모두 홀수이거나 모두 짝수이면, 방향은 0, 2, 4, 6이 되고, 그렇지 않으면 1, 3, 5, 7이 된다.
int start = v[0] == 0 || v[1] == 0 ? 0 : 1;
for (int k = start; k < 8; k += 2) {
balls[r][c][next].add(new Ball(ms, ss, k));
}
}
}
}
}
// 마지막
int total = 0;
for (int i = 0; i < balls.length; i++) {
for (int j = 0; j < balls.length; j++) {
for (int k = 0; k < balls[i][j][K % 2].size(); k++) {
total += balls[i][j][K % 2].get(k).m;
}
}
}
System.out.println(total);
}
private static int checkBoundary(int i) {
if (i < 0)
i += N;
if (i >= N)
i -= N;
return i;
}
}
class Ball {
int m;
int s;
int d;
public Ball(int m, int s, int d) {
super();
this.m = m;
this.s = s;
this.d = d;
}
}
댓글남기기