[BOJ] 16236. 아기 상어

하르미온느·2022년 8월 31일
0

문제풀이

목록 보기
9/11

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

🚨 실력이 뛰어나지 않아 코드가 다소 지저분할 수 있습니다! 🚨

문제

N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다.

아기 상어와 물고기는 모두 크기를 가지고 있고, 이 크기는 자연수이다. 가장 처음에 아기 상어의 크기는 2이고, 아기 상어는 1초에 상하좌우로 인접한 한 칸씩 이동한다.

아기 상어는 자신의 크기보다 큰 물고기가 있는 칸은 지나갈 수 없고, 나머지 칸은 모두 지나갈 수 있다. 아기 상어는 자신의 크기보다 작은 물고기만 먹을 수 있다. 따라서, 크기가 같은 물고기는 먹을 수 없지만, 그 물고기가 있는 칸은 지나갈 수 있다.

아기 상어가 어디로 이동할지 결정하는 방법은 아래와 같다.

더 이상 먹을 수 있는 물고기가 공간에 없다면 아기 상어는 엄마 상어에게 도움을 요청한다.
먹을 수 있는 물고기가 1마리라면, 그 물고기를 먹으러 간다.
먹을 수 있는 물고기가 1마리보다 많다면, 거리가 가장 가까운 물고기를 먹으러 간다.
거리는 아기 상어가 있는 칸에서 물고기가 있는 칸으로 이동할 때, 지나야하는 칸의 개수의 최솟값이다.
거리가 가까운 물고기가 많다면, 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기를 먹는다.
아기 상어의 이동은 1초 걸리고, 물고기를 먹는데 걸리는 시간은 없다고 가정한다. 즉, 아기 상어가 먹을 수 있는 물고기가 있는 칸으로 이동했다면, 이동과 동시에 물고기를 먹는다. 물고기를 먹으면, 그 칸은 빈 칸이 된다.

아기 상어는 자신의 크기와 같은 수의 물고기를 먹을 때 마다 크기가 1 증가한다. 예를 들어, 크기가 2인 아기 상어는 물고기를 2마리 먹으면 크기가 3이 된다.

공간의 상태가 주어졌을 때, 아기 상어가 몇 초 동안 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 공간의 크기 N(2 ≤ N ≤ 20)이 주어진다.

둘째 줄부터 N개의 줄에 공간의 상태가 주어진다. 공간의 상태는 0, 1, 2, 3, 4, 5, 6, 9로 이루어져 있고, 아래와 같은 의미를 가진다.

0: 빈 칸
1, 2, 3, 4, 5, 6: 칸에 있는 물고기의 크기
9: 아기 상어의 위치
아기 상어는 공간에 한 마리 있다.

출력

첫째 줄에 아기 상어가 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는 시간을 출력한다.

풀이

  • 먹이 우선순위는 Comparable 인터페이스를 이용하여 구현 (거리 > row > col)
  • BFS를 통해 상어가 현재 위치에서 가능한 곳으로 모두 이동, 먹을 수 있는 물고기는 다 리스트에 담아 놓음
  • 먹을 수 있는 물고기 리스트를 정렬해서 가장 가까운 것을 먹고, 성장할 수 있을 경우 성장한 후, 리스트 비우기
  • 2, 3번째 과정 반복
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Boj16236 {
    /*
     * 처음 아기상어의 크기는 2
     * 1초에 한칸씩 사방탐색
     * 자신의 크기보다 큰 물고기는 못지나감
     * 크기가 같은 물고기는 지나갈 수 있지만, 먹을 수 없음
     * 크기가 작은 물고기는 지나갈 수 있고, 먹을 수 있음
     *
     * 먹을 수 있는 물고기가 많다면 가장 가까운 것을 먹으러 감 (거리: 물고기까지 가야 하는 칸)
     * 거리가 가까운 물고기가 많다면 가장 위에 있는 물고기, 그런 고기도 많다면 가장 왼쪽에 있는 물고기 먹는다
     * 자신의 크기와 같은 수의 물고기를 먹을때마다 크기가 1 증가한다
     * 더이상 먹을 수 있는 물고기가 없으면 종료
     */

    static class Fish implements Comparable<Fish> {
        int r, c, dist;

        public Fish(int r, int c, int dist) {
            this.r = r;
            this.c = c;
            this.dist = dist;
        }

        public int compareTo(Fish o) {
            // 0. 거리가 가까운 것부터
            if (this.dist == o.dist) {
                    // 1. 가장 위쪽 (row 비교)
                    if (this.r == o.r) {
                        // 2. 가장 왼쪽 (col 비교)
                        return this.c - o.c;
                    }
                    return this.r - o.r;
            }
            return this.dist - o.dist;
        }
    }

    static int n, ans, size = 2, eatCnt = 0; // ans: 상어의 시간 저장
    static int[] dr = {-1, 0, 1, 0}, dc = {0, -1, 0, 1};
    static int[][] map;
    static Fish shk;
    static ArrayList<Fish> fishes = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        System.setIn(new FileInputStream("src/input.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        n = Integer.parseInt(br.readLine());
        map = new int[n][n];
        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());
                if (map[i][j] == 9) {
                    shk = new Fish(i, j, 0);
                    map[i][j] = 0;
                }
            }
        }

        babyShark();
        System.out.println(ans);

    }

    static void babyShark() {
        Queue<Fish> shark = new LinkedList<>();
        boolean[][] vis = new boolean[n][n];
        shark.add(shk);
        vis[shk.r][shk.c] = true;

        while (true) {
            while (!shark.isEmpty()) {
                // 1. BFS를 통해 현재 위치에서 먹을 수 있는 물고기 탐색 (자신보다 크기 작은 것) && 거리 기록
                Fish now = shark.poll();

                for (int i = 0; i < 4; i++) {
                    int nr = now.r + dr[i];
                    int nc = now.c + dc[i];

                    if (nr >= 0 && nr < n && nc >= 0 && nc < n && !vis[nr][nc]) {
                        // 이동 가능
                        if (map[nr][nc] <= size) {
                            shark.add(new Fish(nr, nc, now.dist + 1));
                            vis[nr][nc] = true;

                            // 먹기 가능
                            if (map[nr][nc] >= 1 && map[nr][nc] < size) {
                                fishes.add(new Fish(nr, nc, now.dist + 1));
                            }
                        }
                    }
                }
            }

            // 거리 순 나열
            if (!fishes.isEmpty()) {
                eat();
                for (int i = 0; i < n; i++) {
                    for (int j = 0; j < n; j++) {
                        vis[i][j] = false;
                    }
                }
                shark.add(shk);
            } else return;
        }
    }

    static void eat() {
        // 2. 물고기 중 거리 재서 가장 가까운 거 먹기
        Collections.sort(fishes);

        Fish f = fishes.get(0);

        // 먹기
        shk.r = f.r;
        shk.c = f.c;
        ans += f.dist;
        map[f.r][f.c] = 0;

        if (++eatCnt == size) {
            size++;
            eatCnt = 0;
        }

        fishes.clear();
    }

}

처음에는 상어가 물고기를 탐색하고, 먹는 과정을 모두 하나의 메서드에 넣었는데 알듯말듯한 오차가 조금씩 생겨서 몇시간을 고생했다...
기능별로 모듈화를 하니 보다 깔끔하게 구현할 수 있었다.
객체지향언어를 사용하는 만큼 앞으로도 잘 활용해보도록 하자~! ^ㅁ^

profile
바쁘다 바빠 현대사회 🤪

0개의 댓글