백준 20056
🔥 백준 20056 : 마법사 상어와 파이어볼
https://www.acmicpc.net/problem/20056
20056번: 마법사 상어와 파이어볼
첫째 줄에 N, M, K가 주어진다. 둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다. 서로 다른 두 파이어볼의 위치
www.acmicpc.net
🔥 삼성 기출인 상어 시리즈 문제이다.
🔥 파이어볼 클래스를 하나 만들고, 위치 정보 + 방향, 속력, 질량을 필드로 선언해주었다.
private static class Fireball {
int x;
int y;
int mass;
int speed;
int dir;
public Fireball(int x, int y, int m, int s, int d) {
this.x = x;
this.y = y;
this.mass = m;
this.speed = s;
this.dir = d;
}
}
🔥 구현해야할 기능
- 각 파이어볼들을 이동 시켜주기 : moveFireball()
- map을 보고, 파이어볼 2개 이상인 칸이라면 파이어볼들을 합쳐주기 : joinFireball()
- 이때 단순 질량과 속도는 합한다음 나눠주면 되는데, 문제는 방향이다.
- 마법사 상어와 복제마법처럼 모든 방향이 다 필요한 건 아니고, 각 방향이 다 짝수 || 홀수인지만 파악하면된다.
- 따라서 방향까지 저장하는 3차원 배열을 만들되, int [size][size][2]로 선언해주었다. 방향이 짝수인 파이어볼, 홀수인 파이어볼 각 개수들만 카운트해두면 된다.
- 합쳐진 파이어볼들을 나눠주기 : divideFireball()
- 위에서 계산한 질량, 속도, 방향 기준으로 파이어볼 4개로 다시 만들어주면 된다.
- 그 외, 이 파이어볼은 맵을 벗어나게 이동한다면 재조정이 된다. : rePos()
- 예를 들어 (0, 0)에서 위로 2칸 이동하면 (-2, 0)이 되는데, 4*4 사이즈 맵에서는 이를 (2, 0)로 조정해줘야 한다.
- 이 재조정을 위한 메소드도 구현해두어야 한다.
🔥 각 함수들은 아래와 같은 순서로 호출된다.
private static void magic() {
moveFireball();
joinFireball();
divideFireball();
}
🔥 파이어볼 움직이기
private static void moveFireball() {
List<Fireball> list = new ArrayList<>();
for (Fireball f : fireballs) {
int nextX = f.x + (dirX[f.dir] * f.speed);
nextX = rePos(nextX);
int nextY = f.y + (dirY[f.dir] * f.speed);
nextY = rePos(nextY);
list.add(new Fireball(nextX, nextY, f.mass, f.speed, f.dir));
}
fireballs = list;
}
🔥 이 때 rePos 함수를 통해 맵의 범위를 벗어난 경우에 재조정을 해준다.
rePos는 아래와 같다.
private static int rePos (int pos) {
int temp = pos;
if (pos >= size) {
temp = (pos % size);
}
else if (pos < 0){
if (Math.abs(temp) % size == 0) {
temp = 0;
} else {
int left = Math.abs(temp) / size + 1;
temp += (left * size);
}
}
return temp;
}
🔥 rePos 함수 작동 원리
- 양수인 경우에는 그냥 size로 나눈 나머지로 재조정을 해주면 된다.
- 음수인 경우에는, 절대값으로 바꾼 후, 계산을 해줘야 한다.
- 절대값으로 바꾸고 size로 나눈 나머지가 0이라면 안 움직이면 된다.
- 그 외에는 절대값으로 바꾼 값 / size + 1 을 해준다음 원래의 마이너스 값에 더해주면 된다 .
- 4 * 4 사이즈 라고 생각하며 계산해보았다. 예시를 보면 무슨 규칙인지 이해가 될 것이다.
바뀌기 전 | 바꾼 후 | 바뀌기 전 | 바꾼 후 |
-1 | +3 1 % 4 == 1이므로, 0이 아니다. 1 / 4 + 1 = 1 -3 + ( 4 * 1 ) = 3 |
-5 | +3 |
-2 | +2 | -6 | +2 |
-3 | +1 | -7 | +1 7 % 4 == 3이므로, 0이 아님 7 / 4 + 1 = 2 -7 + (4 * 2) = 1 |
-4 | +0 4 % 4 == 0 절대값으로 바꾸고 남은 나머지가 0이므로 0 |
-8 | +0 |
🔥 파이어볼 합치기
만약 현재 map이 빈칸이라면 그냥 넣기
map에 이미 fireball이 존재한다면 기존의 fireball과 합친다.
사실 이때, 굳이 좌표정보를 넣지 말고 그냥 mass, speed, direction만 모은 클래스를 만들었다면 좀 더 효율적으로 코드를 만들 수 있었을 것 같다.
쨋든 그렇게 합치고, 각 방향별로 짝수 방향이면 dir[x][y][0]의 count를, 홀수방향이라면 dir[x][y][1]의 count를 늘린다.
private static void joinFireball() {
map = new Fireball[size][size];
dir = new int[size][size][2]; // 각 방향들만 저장하는 용도
for (Fireball f : fireballs) {
if (map[f.x][f.y] == null) {
map[f.x][f.y] = f;
}
else {
map[f.x][f.y] = new Fireball(
f.x, f.y,
f.mass + map[f.x][f.y].mass,
f.speed + map[f.x][f.y].speed,
f.dir
);
}
if (f.dir % 2 == 0) {
dir[f.x][f.y][0]++;
} else {
dir[f.x][f.y][1]++;
}
}
}
🔥 합쳐진 파이어볼 다시 나눠주기 : divideFireball()
나눠주기 전에 count 변수로 각 칸에 몇 개의 파이어볼이 합쳐진 것인지 먼저 판단한다. count >=2인 경우만 나눠서 발산해줘야하므로!
그다음 index 변수로 방향을 1-3-5-7로 갈지, 0-2-4-6으로 갈지 설정한다.
그리고 map에서 칸이 null이 아닌 경우에 한정하여
- count >= 2라면 mass, speed 계산해준다음 앞에서 구한 index += 2대로 fireball 리스트에 담아준다.
- count 1인 경우는 mass speed 재 계산 없이 바로 리스트에 더해주면 된다.
private static void divideFireball() {
fireballs = new ArrayList<>();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
int count = dir[i][j][0] + dir[i][j][1];
int index = 1;
if (dir[i][j][0] == 0 || dir[i][j][1]==0) {
index = 0; // 짝수 방향으로 가야 하는 경우
}
if (map[i][j] != null) {
if (count >= 2) {
int mass = (int) Math.floor((double) map[i][j].mass / 5.0);
int speed = (int) Math.floor((double) map[i][j].speed / count);
if (mass != 0) {
for (int k = index; k < 8; k+=2) {
fireballs.add(new Fireball(i,j, mass, speed, k));
}
}
} else {
fireballs.add(map[i][j]);
}
}
}
}
}
🔥 이 과정을 다 거친 후, 마지막으로 fireball(파이어볼을 담는 리스트) 돌면서 질량들만 쭉 더해주면 된다.
for (Fireball fireball : fireballs) {
answer += fireball.mass;
}
🔥 전체코드
import java.io.*;
import java.util.*;
public class p20056 {
static int dirX[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
static int dirY[] = { 0, 1, 1, 1, 0, -1, -1, -1 };
static int size, fire, practice;
static Fireball[][] map;
static int[][][] dir;
static List<Fireball> fireballs;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
size = Integer.parseInt(st.nextToken());
fire = Integer.parseInt(st.nextToken());
practice = Integer.parseInt(st.nextToken());
fireballs = new ArrayList<>();
map = new Fireball[size][size];
for (int i = 0; i < fire; i++) {
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
int s = Integer.parseInt(st.nextToken());
int d = Integer.parseInt(st.nextToken());
fireballs.add(new Fireball(x-1, y-1, m, s, d));
}
for (int i = 0; i < practice; i++) {
magic();
}
int answer = 0;
for (Fireball fireball : fireballs) {
answer += fireball.mass;
}
System.out.println(answer);
}
private static void magic() {
moveFireball();
joinFireball();
divideFireball();
}
private static void divideFireball() {
fireballs = new ArrayList<>();
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
int count = dir[i][j][0] + dir[i][j][1];
int index = 1;
if (dir[i][j][0] == 0 || dir[i][j][1]==0) {
index = 0; // 짝수 방향으로 가야 하는 경우
}
if (map[i][j] != null) {
if (count >= 2) {
int mass = (int) Math.floor((double) map[i][j].mass / 5.0);
int speed = (int) Math.floor((double) map[i][j].speed / count);
if (mass != 0) {
for (int k = index; k < 8; k+=2) {
fireballs.add(new Fireball(i,j, mass, speed, k));
}
}
} else {
fireballs.add(map[i][j]);
}
}
}
}
}
private static void joinFireball() {
map = new Fireball[size][size];
dir = new int[size][size][2]; // 각 방향들만 저장하는 용도
for (Fireball f : fireballs) {
if (map[f.x][f.y] == null) {
map[f.x][f.y] = f;
}
else {
map[f.x][f.y] = new Fireball(
f.x, f.y,
f.mass + map[f.x][f.y].mass,
f.speed + map[f.x][f.y].speed,
f.dir
);
}
if (f.dir % 2 == 0) {
dir[f.x][f.y][0]++;
} else {
dir[f.x][f.y][1]++;
}
}
}
private static void moveFireball() {
List<Fireball> list = new ArrayList<>();
for (Fireball f : fireballs) {
int nextX = f.x + (dirX[f.dir] * f.speed);
nextX = rePos(nextX);
int nextY = f.y + (dirY[f.dir] * f.speed);
nextY = rePos(nextY);
list.add(new Fireball(nextX, nextY, f.mass, f.speed, f.dir));
}
fireballs = list;
}
private static int rePos (int pos) {
int temp = pos;
if (pos >= size) {
temp = (pos % size);
}
else if (pos < 0){
if (Math.abs(temp) % size == 0) {
temp = 0;
} else {
int left = Math.abs(temp) / size + 1;
temp += (left * size);
}
}
return temp;
}
private static class Fireball {
int x;
int y;
int mass;
int speed;
int dir;
public Fireball(int x, int y, int m, int s, int d) {
this.x = x;
this.y = y;
this.mass = m;
this.speed = s;
this.dir = d;
}
}
}