-
백준 20056Java/코딩테스트 2023. 10. 10. 18:51
🔥 백준 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; } } }
'Java > 코딩테스트' 카테고리의 다른 글
백준 20057 (0) 2023.10.12 백준 21608 (0) 2023.10.11 프로그래머스 프로세스, 퍼즐조각 채우기, 올바른 괄호 (0) 2023.10.03 프로그래머스 전력망을 둘로 나누기, 베스트앨범, 의상 (0) 2023.09.26 백준 2133, 2141 (0) 2023.09.20