본문 바로가기
Algorithm(알고리즘)/Java

[Java][백준 20056][시뮬레이션][삼성SW 역량 테스트] 마법사 상어와 파이어볼

by Jun_N 2021. 4. 15.

어른 상어가 마법사가 되었고, 파이어볼을 배웠다.

마법사 상어가 크기가 N×N인 격자에 파이어볼 M개를 발사했다. 가장 처음에 파이어볼은 각자 위치에서 이동을 대기하고 있다. i번 파이어볼의 위치는 (ri, ci), 질량은 mi이고, 방향은 di, 속력은 si이다. 위치 (r, c)는 r행 c열을 의미한다.

격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.

파이어볼의 방향은 어떤 칸과 인접한 8개의 칸의 방향을 의미하며, 정수로는 다음과 같다.

7 0 1
6   2
5 4 3

마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.

  1. 모든 파이어볼이 자신의 방향 di로 속력 si칸 만큼 이동한다.
    • 이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
  2. 이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
    1. 같은 칸에 있는 파이어볼은 모두 하나로 합쳐진다.
    2. 파이어볼은 4개의 파이어볼로 나누어진다.
    3. 나누어진 파이어볼의 질량, 속력, 방향은 다음과 같다.
      1. 질량은 ⌊(합쳐진 파이어볼 질량의 합)/5⌋이다.
      2. 속력은 ⌊(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)⌋이다.
      3. 합쳐지는 파이어볼의 방향이 모두 홀수이거나 모두 짝수이면, 방향은 0, 2, 4, 6이 되고, 그렇지 않으면 1, 3, 5, 7이 된다.
    4. 질량이 0인 파이어볼은 소멸되어 없어진다.

마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 구해보자.

입력

첫째 줄에 N, M, K가 주어진다.

둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다.

서로 다른 두 파이어볼의 위치가 같은 경우는 입력으로 주어지지 않는다.

출력

마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 출력한다.

 


🌈 풀이 후기

  • 삼성 2020 역량테스트에 나온 문제이다. 
  • 삼성 서합을 해서 삼성 코테볼때까지 최근 기출문제를 다 풀어보려고 한다!!! 근데 시뮬레이션은 머리가 너무 복잡해서 진이 빠진다..
  • 처음에 Map에 2차원 배열을 어떻게 저장할까 고민이였는데 .. Fireball을 기억하는 ArrayList와 현재 Fireball이 있는 위치를 기억하는 Map을 두개를 각각 사용하면 되었다. 나중에 비슷한 문제를 풀때 이 방법을 사용하도록 하자!!

👩‍🏫 문제 풀이

  1. Fireball은 ArrayList인 list에 저장한다. 
  2. move를 해주면서 이동한 위치에 2차원 ArrayList인 Map에 현재 Fireball을 추가해준다. 
    *' 이때, fireball의 위치는 업데이트 해줘야 하고 N*N이 범위를 초과할때 파이어볼이 사라지는게 아니라 N*N이 반복되는 구조라서 %를 잘 활용해준다.
  3. Map이 1이면 파이어볼을 나눌 필요가 없으므로 continue 해주는데, 이때 map은 초기화 해준다. (왜냐면 나중에 map에 계속 새로운 파이어볼 위치를 추가해줘야 되기 때문에, 기존의 파이버볼은 list로 따로 관리하기 때문에 진짜로 사라지진 않는다..)
  4. 그 위치에 있는 파이어볼이 모두 짝수거나 홀수인경우를 찾은 다음에 잘 나눠주면 된다.
package SWTest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class BOJ_G5_20056_마법사상어와파이어볼 {
	static class Fireball {
		int r, c, m, s, d;

		public Fireball(int r, int c, int m, int s, int d) {
			super();
			this.r = r;
			this.c = c;
			this.m = m;
			this.s = s;
			this.d = d;
		}

	}

	static int N, M, K;
	static ArrayList<Fireball>[][] map;
	static ArrayList<Fireball> list = new ArrayList<>();
	static int[] dr = { -1, -1, 0, 1, 1, 1, 0, -1 };
	static int[] dc = { 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());
		map = new ArrayList[N][N];
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				map[i][j] = new ArrayList<Fireball>();
			}
		}

		int s1, s2, s3, s4, s5;
		for (int i = 0; i < M; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			s1 = Integer.parseInt(st.nextToken()) - 1;
			s2 = Integer.parseInt(st.nextToken()) - 1;
			s3 = Integer.parseInt(st.nextToken());
			s4 = Integer.parseInt(st.nextToken());
			s5 = Integer.parseInt(st.nextToken());
			list.add(new Fireball(s1, s2, s3, s4, s5));
		}

		for (int time = 0; time < K; time++) {
			move();
			checkFireball();
		}
		int answer = 0;
		for (Fireball cur : list)
			answer += cur.m;
		System.out.println(answer);
	}

	// 2개 이상인 파이어볼 나누기 검사
	private static void checkFireball() {
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (map[i][j].size() == 1)
					map[i][j].clear(); // map은 초기화하고 fireball은 list에 있으니 상관없음. 다음 move에서 움직일 알아서 예정.
				if (map[i][j].size() < 2)
					continue;

				int massSum = 0, sSum = 0;

				boolean even = map[i][j].get(0).d % 2 == 0 ? true : false;
				boolean odd = map[i][j].get(0).d % 2 == 1 ? true : false;
				
				for (Fireball cur : map[i][j]) {
					massSum += cur.m;
					sSum += cur.s;
					even = even & cur.d % 2 == 0 ? true : false;
					odd = odd & cur.d % 2 == 1 ? true : false;
					list.remove(cur);
				}
				int newMass = massSum / 5;
				int size = map[i][j].size();
				map[i][j].clear();

				if (newMass == 0)
					continue;
				int newS = sSum / size;
				if (even | odd) { // 모두 홀수거나, 모두 짝수일때
					for (int k = 0; k < 8; k += 2) {
						list.add(new Fireball(i, j, newMass, newS, k));
					}
				} else { // 홀짝 섞여 있을 경우 (false,false)
					for (int k = 1; k < 8; k += 2) {
						list.add(new Fireball(i, j, newMass, newS, k));
					}
				}

			}
		}
	}

	// fireball 이동
	private static void move() {

		for (Fireball fb : list) {
			int nr = (fb.r + N + dr[fb.d] * (fb.s % N)) % N; // out이 아니라 연결되어있어서 다시 반복됨 0 1 2 0 1 2 ..
			int nc = (fb.c + N + dc[fb.d] * (fb.s % N)) % N;

			// update
			fb.r = nr;
			fb.c = nc;
			map[nr][nc].add(fb);
		}

	}
}