본문 바로가기
알고리즘

[백준] 21610 마법사 상어와 비바라기 - java

by minkang 2021. 6. 25.

https://www.acmicpc.net/problem/21610

 

21610번: 마법사 상어와 비바라기

마법사 상어는 파이어볼, 토네이도, 파이어스톰, 물복사버그 마법을 할 수 있다. 오늘 새로 배운 마법은 비바라기이다. 비바라기를 시전하면 하늘에 비구름을 만들 수 있다. 오늘은 비바라기

www.acmicpc.net

public class 마법사상어와비바라기 {
	static int N, M;
	static int map[][];
	static boolean cloud[][];

	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());
		map = new int[N + 1][N + 1];
		cloud = new boolean[N + 1][N + 1]; // 구름의 상태 정보
		for (int i = 1; i <= N; i++) {
			st = new StringTokenizer(br.readLine());
			for (int j = 1; j <= N; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
			}
		}

		initCloud();
		for (int i = 0; i < M; i++) {
			st = new StringTokenizer(br.readLine());
			// 1.
			moveCloud(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
			// 2.
			drawRain();
			// 3. 물복사
			copyWater();
			// 4. 구름 갱신
			refreshCloud();
		}

		System.out.println(sumWater());
	}

	private static int sumWater() {
		int sum = 0;
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				sum += map[i][j];
			}
		}
		return sum;
	}

	private static void refreshCloud() {
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				if (cloud[i][j] == true) {
					cloud[i][j] = false;
				} else if (map[i][j] >= 2) {
					cloud[i][j] = true;
					map[i][j] -=2;
				}
			}
		}
	}

	private static void copyWater() {
		int dx[] = { -1, -1, 1, 1 }; // 대각선 영역만 검사
		int dy[] = { -1, 1, 1, -1 };

		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				if (cloud[i][j] == true) {
					int sum = 0;
					for (int d = 0; d < 4; d++) {
						int nx = i + dx[d];
						int ny = j + dy[d];
						if (nx < 1 || nx > N || ny < 0 || ny > N)
							continue;
						if (map[nx][ny] > 0) // 물이 없었는데 더해져서 생기는 경우를 체크할 필요가 없다
							sum++;
					}
					map[i][j] += sum;
				}
			}
		}
	}

	private static void drawRain() {
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				if (cloud[i][j] == true) {
					map[i][j]++;
				}
			}
		}
	}

	private static void moveCloud(int d, int s) {
		// 왼쪽부터 시계방향
		int dx[] = { 0,0, -1, -1, -1, 0, 1, 1, 1 };
		int dy[] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };

		boolean tempCloud[][] = new boolean[N+1][N+1];
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				if (cloud[i][j] == true) {
					int nx = (i+dx[d]*s)%N;
					int ny = (j+dy[d]*s)%N;
					
					if(nx == 0) nx = N;
					else if(nx < 0 ) nx= N+nx;
					
					if(ny == 0) ny = N;
					else if(ny < 0) ny = N+ny;
					tempCloud[nx][ny] = true;
				}
			}
		}
		cloud = tempCloud;
	}

	private static void initCloud() {
		cloud[N][1] = true;
		cloud[N][2] = true;
		cloud[N - 1][1] = true;
		cloud[N - 1][2] = true;
	}
}

간단한 시뮬레이션 문제였습니다.

우선 맵의 정보를 나타낼 2차원 배열 map[][]과 구름의 위치를 나타낼 cloud[][] 를 선언했습니다.

입력값을 다 받은 후

 

첫번째로 구름 이동을 하는 moveCloud 메서드입니다.

조건으로는 cloud 배열을 통해 검사하고

이동한 값을 tempCloud 배열에 저장한 다음 cloud를 tempCloud로 갱신해주었습니다.

 

여기서 구름 이동은 N=8이라고 하였을 때 1 ~ 8 을 기준으로

-15 ~ -8

 -7 ~ 0

  1 ~ 8   

  9 ~ 16

 17 ~ 24

 

0보다 클때는 nx = ( 현재위치 + 이동거리 ) % N 이고

0보다 작을때는 nx = N + ( 현재위치 +이동거리 ) % N 입니다.

그 다음 둘다 nx가 0일 때는 N으로 갱신해주어야 합니다.

 

나머지 로직들은 간단하게 구현할 수 있었습니다.