Algorithm/Problem
마법사 상어와 비바라기(BJ_G5_21610)
당진개발자
2024. 2. 24. 17:52
1. 문제 링크
https://www.acmicpc.net/problem/21610
21610번: 마법사 상어와 비바라기
마법사 상어는 파이어볼, 토네이도, 파이어스톰, 물복사버그 마법을 할 수 있다. 오늘 새로 배운 마법은 비바라기이다. 비바라기를 시전하면 하늘에 비구름을 만들 수 있다. 오늘은 비바라기
www.acmicpc.net
2. 나의 코드
메모리: 22864kb
시간: 264ms
코드 길이: 3527B
시간 복잡도 : O(M * N^2)
설명
- 모든 구름을 이동
- 이동한 좌표를 방문처리
- 이동한 곳을 기준으로 대간선으로 탐
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class BJ_21610 {
static class Cloud {
int r, c;
public Cloud(int r, int c) {
this.r = r;
this.c = c;
}
}
static int[] dr = {0, 0, -1, -1, -1, 0, 1, 1, 1};
static int[] dc = {0, -1, -1, 0, 1, 1, 1, 0, -1};
static int[] diagonal = {2, 4, 6, 8}; // 대각선의 인덱스 번호
static int N, M; // N : 가로,세로 길이 M : 이동 횟수
static int[][] water; // 물의 양을 저장하는 배열
static Queue<Cloud> clouds; // 이동할 구름들
static boolean[][] visited; // 이동을 마친 구름들의 좌표
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());
water = new int[N][N];
clouds = new LinkedList<>();
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
water[i][j] = Integer.parseInt(st.nextToken());
}
}
// 최초 구름 생성
for (int i = N - 2; i < N; i++) for (int j = 0; j < 2; j++) clouds.offer(new Cloud(i, j));
// 명령 시작
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
solve(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
}
System.out.println(sumCloud());
}
private static int sumCloud() {
return Arrays.stream(water)
.flatMapToInt(Arrays::stream)
.sum();
}
// 구름 이동시켜서 비를 내리게 하고 구름을 없애는 메서드
private static void solve(int d, int s) { // d : 방향, s : 거리
visited = new boolean[N][N];
while (!clouds.isEmpty()) {
Cloud nowCloud = clouds.poll();
int nr = (nowCloud.r + N + dr[d] * (s % N)) % N;
int nc = (nowCloud.c + N + dc[d] * (s % N)) % N;
visited[nr][nc] = true; // 이동한 곳의 좌표를 방문처리
water[nr][nc] += 1; // 이동한 곳 비내리기
}
// 이동한 구름들의 대각선을 확인 후 비내리게 하기
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (visited[i][j]) {
for (int idx : diagonal) {
int nr = i + dr[idx];
int nc = j + dc[idx];
if (isValid(nr, nc) && water[nr][nc] != 0) ++water[i][j];
}
}
}
}
// 구름 생성하기
makeCloud();
}
// 구름 생성하는 메서드
private static void makeCloud() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (!visited[i][j] && water[i][j] >= 2) {
clouds.offer(new Cloud(i, j));
water[i][j] = Math.max((water[i][j] - 2), 0);
}
}
}
}
// 맵을 벗어났는지 여부 체크
private static boolean isValid(int r, int c) {
return 0 <= r && r < N && 0 <= c && c < N;
}
}
3. 정답 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
static int n, m, map[][];
static int[] dx = { 0, -1, -1, -1, 0, 1, 1, 1 };
static int[] dy = { -1, -1, 0, 1, 1, 1, 0, -1 };
static List<Cloud> list; // 구름을 관리하는 리스트
static boolean[][] isCloud; // 삭제된 구름의 위치에 구름을 생성하지 않도록 상태 저장 하는 배열
static class Cloud {
int x;
int y;
Cloud(int x, int y) {
this.x = x;
this.y = y;
}
}
static void moveAndRain(int d, int s) { // 이동 후 비를 내림
isCloud = new boolean[n][n];
for (Cloud cloud : list) {
cloud.x = (n + cloud.x + (dx[d] * s) % n) % n; // 이동
cloud.y = (n + cloud.y + (dy[d] * s) % n) % n; // 이동
map[cloud.x][cloud.y]++; // 이동된 위치에 비 내림
isCloud[cloud.x][cloud.y] = true; // 위치 저장
}
copyWater(); // 물 복사 버그
list.clear(); // 구름 다 삭제
}
static void copyWater() { // 물을 복사
for (Cloud cloud : list) {
for (int i = 0; i < 4; i++) {
int nx = cloud.x + dx[i * 2 + 1];
int ny = cloud.y + dy[i * 2 + 1];
if (isIn(nx, ny) && map[nx][ny] != 0) { // 대각선 방향으로 물이 있으면
map[cloud.x][cloud.y]++; // 물 추가
}
}
}
}
static boolean isIn(int x, int y) {
return x >= 0 && y >= 0 && x < n && y < n;
}
static void makeCloud() { // 구름을 생성
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (map[i][j] >= 2 && !isCloud[i][j]) { // 물이 2이상이 있고 구름이 있던 위치가 아니면
list.add(new Cloud(i, j)); // 구름 생성
map[i][j] -= 2;
}
}
}
}
static int getWater() { // 구름의 총합
int water = 0;
for (int i = 0; i < n; i++) {
water += Arrays.stream(map[i]).sum();
}
return water;
}
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][];
list = new ArrayList<>();
for (int i = 0; i < n; i++) {
map[i] = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();
}
for (int i = n - 2; i < n; i++) {
for (int j = 0; j < 2; j++) {
list.add(new Cloud(i, j));
}
}
while (m-- > 0) {
st = new StringTokenizer(br.readLine());
int d = Integer.parseInt(st.nextToken()) - 1;
int s = Integer.parseInt(st.nextToken());
moveAndRain(d, s);
makeCloud();
}
int answer = getWater();
System.out.println(answer);
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static int N, M, answer;
static int[] dr = {0, -1, -1, -1, 0, 1, 1, 1};
static int[] dc = {-1, -1, 0, 1, 1, 1, 0, -1};
static int[][] map;
static Queue<Cloud> clouds;
static Move[] moves;
static class Cloud {
int r, c;
public Cloud(int r, int c) {
this.r = r;
this.c = c;
}
}
static class Move {
int d, s;
public Move(int d, int s) {
this.d = d;
this.s = s;
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
StringTokenizer st;
// 초기 세팅
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
map = new int[N][N];
moves = new Move[M];
clouds = new ArrayDeque<>();
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
}
}
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
moves[i] = new Move(Integer.parseInt(st.nextToken()) - 1, Integer.parseInt(st.nextToken()));
}
play();
// 바구니에 들어있는 물의 양 합 구하기
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
answer += map[i][j];
}
}
System.out.println(answer);
}
private static void play() {
boolean[][] visited;
// 비바라기 시전
for (int i = N - 2; i < N; i++) {
for (int j = 0; j < 2; j++) {
clouds.offer(new Cloud(i, j));
}
}
// M번 명령 반복
for (int time = 0; time < M; time++) {
int d = moves[time].d;
int s = moves[time].s;
int cloudsSize = clouds.size();
visited = new boolean[N][N];
// 생성된 구름 개수만큼 반복
for (int idx = 0; idx < cloudsSize; idx++) {
Cloud cloud = clouds.poll();
// 1. 모든 구름이 d방향으로 s칸 이동한다.
int nr = cloud.r + dr[d] * (s % N);
int nc = cloud.c + dc[d] * (s % N);
if (nr > 0) {
nr %= N;
} else if (nr < 0) {
nr += N;
}
if (nc > 0) {
nc %= N;
} else if (nc < 0) {
nc += N;
}
cloud.r = nr;
cloud.c = nc;
// 2~3. 구름에서 비가 내려 바구니의 물이 1 증가하고 구름이 사라진다.
map[nr][nc]++;
visited[nr][nc] = true;
}
// 4. 물복사버그 마법을 시전한다.
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (visited[i][j]) {
for (int dir = 1; dir < 8; dir += 2) {
if (isIn(i + dr[dir], j + dc[dir]) && map[i + dr[dir]][j + dc[dir]] > 0) {
map[i][j]++;
}
}
}
}
}
// 5. 구름이 생기고 물의 양이 줄어든다.
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (map[i][j] >= 2 && !visited[i][j]) {
clouds.offer(new Cloud(i, j));
map[i][j] -= 2;
}
}
}
}
}
private static boolean isIn(int r, int c) {
return r >= 0 && c >= 0 && r < N && c < N;
}
}
4. 배운 점
- 전형적인 시물레이션 문제이다. 이렇게 이동하는 객체가 많을 경우는 모두 이동 시킨뒤 후처리 하는게 더 좋은 것 같다.
- 배열의 합을 구할 때는 다음과 같은 Stream을 쓰는 연습을 많이 하면 좋을 것 같다.
- Arrays.stream : 배열 >> 스트림
- flatMaptoInt : 2차원 배열 >> 1차원 배열
Arrays.stream(water)
.flatMapToInt(Arrays::stream)
.sum();