[백준/BOJ] 16236 아기 상어 - JAVA

2025. 5. 25. 23:55·알고리즘/문제풀이

[백준/BOJ] 16236 아기 상어 - JAVA

문제

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

 

문제 분석

조건

  • NxN 공간에 물고기와 아기 상어가 있다.
  • 상어는 자신보다 크기가 작은 물고기만 먹을 수 있고,
    크기가 같은 물고기는 지나갈 수만 있고,
    더 큰 물고기는 아예 지나가지 못한다.
  • 한 번에 가장 가까운 물고기를 먹으러 간다.
    만약 거리가 같은 물고기가 여러 마리라면,
    ➝ 위쪽, 그다음 왼쪽 물고기를 우선으로 선택한다.
  • 상어는 자신의 크기만큼 물고기를 먹을 때마다 크기가 1 증가한다.
  • 모든 과정을 반복하며, 상어가 더 이상 먹을 물고기가 없을 때까지 걸리는 총 시간을 구하는 문제

풀이방법

  • 가장 가까운 물고기를 구하기 위해 BFS를 사용한다. 그 과정에서 우선순위 큐를 활용하여 거리 -> 위쪽 -> 왼쪽 순으로 정렬되도록 정렬 기준을 정하고, 우선순위 큐를 사용한다.
  • 먹을 수 있는 물고기가 있는 좌표를 구하는 `getNextPoint(현재 좌표)`를 사용하였다. `while`문에서 좌표값이 null로 나오기 전까지 반복한다.
  • 물고기를 먹으면 해당 좌표에 물고기를 지우고 현재 사이즈와 먹은 물고기 수를 비교해 사이즈 조절을 해준다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

class Point implements Comparable<Point> {
    private int r, c, count;

    public Point(int r, int c, int count) {
        this.r = r;
        this.c = c;
        this.count = count;
    }

    public int getR() {
        return r;
    }

    public int getC() {
        return c;
    }

    public int getCount() {
        return count;
    }

    @Override
    public int compareTo(Point o) {
        if (this.count == o.count) {
            if (this.r == o.r) {
                return this.c - o.c;
            }
            return this.r - o.r;
        }
        return this.count - o.count;
    }
}

public class Main {
    static int nowSize, time, eatCount;
    static int[][] map;
    static int N;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        StringBuilder sb = new StringBuilder();

        N = Integer.parseInt(br.readLine());
        map = new int[N][N];
        int[] now = {0, 0};
        for (int r = 0; r < N; r++) {
            st = new StringTokenizer(br.readLine());
            for (int c = 0; c < N; c++) {
                map[r][c] = Integer.parseInt(st.nextToken());
                if (map[r][c] == 9) {
                    now[0] = r;
                    now[1] = c;
                    map[r][c] = 0;
                }
            }
        }

        nowSize = 2;
        time = 0;
        eatCount = 0;
        while ((now = getNextPoint(now)) != null) {     // 이동 가능한 좌표가 있으면 반복
            eatCount++; //물고기 먹기
            map[now[0]][now[1]] = 0;
            if (eatCount == nowSize) {  // 사이즈 만큼 먹었을 경우 크기 자람
                nowSize++;
                eatCount = 0;
            }
        }
        System.out.println(time);
    }

    /**
     * 다음 이동할 좌표(먹어야하는 물고기가 있는 위치)를 구하는 함수
     *
     * @param now 현재 좌표
     * @return 다음 이동 좌표
     */
    private static int[] getNextPoint(int[] now) {
        boolean[][] visited = new boolean[N][N];
        // 거리순, 거리가 같다면 위쪽, 왼쪽 우선순위를 위해 우선순위 큐 사용
        PriorityQueue<Point> que = new PriorityQueue<>();
        que.add(new Point(now[0], now[1], 0));
        visited[now[0]][now[1]] = true;
        int[][] dirs = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
        while (!que.isEmpty()) {
            Point nowPoint = que.poll();

            // 잡아 먹을 수 있는 물고기가 있는 좌표면 좌표 반환
            if (map[nowPoint.getR()][nowPoint.getC()] > 0
                    && map[nowPoint.getR()][nowPoint.getC()] < nowSize) {
                time += nowPoint.getCount();    // 물고기까지 거리(이동시간) 더하기
                return new int[]{nowPoint.getR(), nowPoint.getC()};
            }
            for (int[] dir : dirs) {
                int nextR = nowPoint.getR() + dir[0];
                int nextC = nowPoint.getC() + dir[1];
                if (canMove(nextR, nextC) && !visited[nextR][nextC]) {    // 이동 가능하면 큐에 삽입
                    que.add(new Point(nextR, nextC, nowPoint.getCount() + 1));
                    visited[nextR][nextC] = true;
                }
            }
        }
        return null;
    }

    /**
     * 이동 가능한지 확인(범위 내 빈 곳이거나, 작거나 같은 사이즈의 물고기가 있는 곳)
     *
     * @param nextR
     * @param nextC
     * @return
     */
    private static boolean canMove(int nextR, int nextC) {
        if (nextR < 0 || nextR >= N || nextC < 0 || nextC >= N
                || map[nextR][nextC] > nowSize) {
            return false;
        }
        return true;
    }

}

시간복잡도

BFS 한번 돌 때 마다 최대 맵의 크기인 O(N*N)만큼 소요된다.
먹이를 한번 먹으면 해당 위치에서 다시 탐색하므로 최대 물고기 수인 N*N만큼 반복된다.

728x90
저작자표시 비영리 변경금지 (새창열림)
'알고리즘/문제풀이' 카테고리의 다른 글
  • [프로그래머스] 43105 정수 삼각형 - JAVA
  • [백준/BOJ] 11404 플로이드 - JAVA
  • [백준/BOJ] 7576 토마토 - JAVA
  • [프로그래머스] 43163 단어 변환 - JAVA
LIRI
LIRI
  • LIRI
    기록
    LIRI
  • 전체
    오늘
    어제
    • 분류 전체보기 (74)
      • 블로그 꾸미기 (0)
      • Spring (6)
      • 바이브코딩 (1)
      • React (3)
      • CS (0)
      • 알고리즘 (57)
        • 개념 (2)
        • 문제풀이 (54)
      • Java (1)
      • DB (1)
      • log (4)
        • SSAFY (3)
        • 궁금 (1)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 공지사항

  • 인기 글

  • 태그

    Security
    BFS
    dfs
    SSAFY
    그리디
    바이브코딩
    비트마스킹
    느좋코딩
    JWT
    SSAFY 9기
    BOJ
    최장증가부분수열
    프로그래머스
    dp
    알고리즘 문제풀이
    lv3
    싸피
    리액트
    Springsecurity
    LV2
    백준
    ssafy 합격 후기
    커서ai
    pccp모의고사
    springboot
    Java
    LIS
    알고리즘
    너비우선탐색
    Spring
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
LIRI
[백준/BOJ] 16236 아기 상어 - JAVA
상단으로

티스토리툴바