[백준/BOJ] 19237 어른 상어 - JAVA

2025. 6. 11. 01:07·알고리즘/문제풀이

[백준/BOJ] 19237 어른 상어 - JAVA

문제

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

문제 분석

조건

`N×N`격자 중 M개의 칸에 상어가 한 마리씩 들어있다. 상어는 시작 위치에 냄새를 뿌린다. 모든 상어는 1초마다 상하좌우로 인접한 칸으로 이동하고, 이동한 칸에도 냄새를 뿌린다. 냄새는 `k`초 후에 사라진다.

상어가 이동할 때는 현재 바라보는 방향에서 냄새가 없는 칸으로 이동한다. 인접한 칸에 이동할 수 있는 칸이 여러 군데면 우선순위를 따른다. 냄새가 없는 칸이 없다면 자신의 냄새가 있는 곳으로 이동한다. 이 역시 우선순위를 따른다.

같은 칸에 상어가 여러 마리 존재할 경우 작은 번호의 상어만 남고, 나머지 상어는 없어진다.

풀이방법

상어 클래스를 정의하여 `상어의 위치, 번호, 방향, 생존 여부, 바라보는 방향에 따른 우선순위`를 관리하였다. 지금까지 푸는 상어 문제들은 공통적으로 조건과 상태관리만 주의한다면 풀이 자체는 단순 구현에 가깝다.

 

같은 칸에 상어가 여러 마리 존재한다면 작은 번호의 상어만 남으므로 처음부터 1번 상어부터 이동하도록 하였다. 그 후에 뒷번호 상어가 이동하려는 칸에 이미 다른 상어가 존재한다면, 앞번호 상어가 이미 차지한 자리이므로 바로 죽은 상어로 처리하였다.

if (map[nextR][nextC][SMELL_REMAIN] == 0) {    // 냄새가 없음
    if (map[nextR][nextC][SHARK_NUM] != 0) { // 같은 턴에 앞번호 상어가 이동한 칸임
        shark.isAlive = false;
        sharkCount--;
        map[shark.r][shark.c][SHARK_NUM] = 0;

이때 냄새가 없는 칸만 검사한 이유는 이전 턴에 자리를 차지한(아직 이동하지 않은) 상어가 있는 자리는 제외하기 위해서이다. 그리고 이렇게 구현하기 위해서는 상어가 이동했을 때 바로 상어 위치에 상어의 냄새를 뿌리면 안 된다. 상어의 냄새는 냄새 감소 부분에서 상어가 위치한 칸이면 `k`값의 냄새를 뿌리고, 상어가 없이 냄새가 남아있을 땐 냄새를 감소시키며 구현했다.

코드

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

public class Main {

    static class Shark {
        int r, c, num, nowDir;
        boolean isAlive;
        int[][] dirPriority;

        public Shark(int r, int c, int num) {
            this.r = r;
            this.c = c;
            this.num = num;
            this.isAlive = true;
            this.dirPriority = new int[4][4];
        }

        public void setNowDir(int nowDir) {
            this.nowDir = nowDir;
        }

        public void setDirPriority(int dir, int[] priority) {
            this.dirPriority[dir] = priority;
        }
    }

    static int N, M, k;
    static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    static int SHARK_NUM = 0, SMELL_REMAIN = 1, SMELL_SHARK = 2;

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

        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        k = Integer.parseInt(st.nextToken());
        int[][][] map = new int[N][N][3];   // [r][c][상어 번호, 냄새, 냄새 남긴 상어]
        Shark[] sharks = new Shark[M + 1];
        int sharkCount = M;

        // 지도 입력
        for (int r = 0; r < N; r++) {
            st = new StringTokenizer(br.readLine());
            for (int c = 0; c < N; c++) {
                map[r][c][SHARK_NUM] = Integer.parseInt(st.nextToken());
                if (map[r][c][SHARK_NUM] != 0) {    // 상어일 경우
                    map[r][c][SMELL_REMAIN] = k;
                    map[r][c][SMELL_SHARK] = map[r][c][SHARK_NUM];
                    sharks[map[r][c][SHARK_NUM]] = new Shark(r, c, map[r][c][SHARK_NUM]);
                }
            }
        }

        // 상어 방향
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= M; i++) {
            sharks[i].setNowDir(Integer.parseInt(st.nextToken()) - 1);
        }

        // 상어 이동 우선순위 입력
        for (int shark = 1; shark <= M; shark++) {
            for (int dir = 0; dir < 4; dir++) {
                st = new StringTokenizer(br.readLine());
                int[] priority = new int[4];
                for (int i = 0; i < 4; i++) {
                    priority[i] = Integer.parseInt(st.nextToken()) - 1;
                }
                sharks[shark].setDirPriority(dir, priority);
            }
        }

        int answer = 0;
        while (sharkCount != 1) {
            answer++;
            if (answer > 1000) {
                answer = -1;
                break;
            }

            // 상어 움직임
            for (Shark shark : sharks) {    // 작은 번호의 상어만 남기기 위해 1번부터 이동
                if (shark == null) {
                    continue;
                }
                if (!shark.isAlive) {
                    continue;
                }
                int[] dirPriority = shark.dirPriority[shark.nowDir];
                boolean moveCheck = false;
                for (int dir : dirPriority) {   // 인접한 칸에 빈 칸 있음
                    int nextR = shark.r + dirs[dir][0];
                    int nextC = shark.c + dirs[dir][1];
                    if (isOutOfBounds(nextR, nextC)) {
                        continue;
                    }
                    if (map[nextR][nextC][SMELL_REMAIN] == 0) {    // 냄새가 없음
                        if (map[nextR][nextC][SHARK_NUM] != 0) { // 같은 턴에 앞번호 상어가 이동한 칸임
                            shark.isAlive = false;
                            sharkCount--;
                            map[shark.r][shark.c][SHARK_NUM] = 0;
                        } else {
                            sharkMove(map, shark, dir, nextR, nextC);
                        }
                        moveCheck = true;
                        break;
                    }
                }
                if (moveCheck) {
                    continue;
                }
                // 인접한 칸에 빈칸 없었음 -> 내 냄새칸으로
                for (int dir : dirPriority) {
                    int nextR = shark.r + dirs[dir][0];
                    int nextC = shark.c + dirs[dir][1];
                    if (isOutOfBounds(nextR, nextC)) {
                        continue;
                    }
                    if (map[nextR][nextC][SMELL_SHARK] == shark.num) {    // 본인 냄새가 있는 칸
                        sharkMove(map, shark, dir, nextR, nextC);
                        break;
                    }

                }
            }
            // 냄새 감소
            fadeSmell(map);
        }
        System.out.println(answer);
    }

    private static boolean isOutOfBounds(int nextR, int nextC) {
        return nextR < 0 || nextC < 0 || nextR >= N || nextC >= N;
    }

    private static void fadeSmell(int[][][] map) {
        for (int r = 0; r < N; r++) {
            for (int c = 0; c < N; c++) {
                if (map[r][c][SHARK_NUM] != 0) {    // 지금 상어가 있는 칸임
                    map[r][c][SMELL_REMAIN] = k;
                } else if (map[r][c][SMELL_REMAIN] > 0) {
                    map[r][c][SMELL_REMAIN]--;
                    if (map[r][c][SMELL_REMAIN] == 0) {
                        map[r][c][SMELL_SHARK] = 0;
                    }
                }
            }
        }
    }

    private static void sharkMove(int[][][] map, Shark shark, int dir, int nextR, int nextC) {
        // 기존 위치 비우기
        map[shark.r][shark.c][SHARK_NUM] = 0;
        shark.r = nextR;
        shark.c = nextC;
        //새 위치에 상어번호 넣기
        map[nextR][nextC][SHARK_NUM] = shark.num;
        map[nextR][nextC][SMELL_SHARK] = shark.num;
        shark.nowDir = dir;
    }
}

 

더보기

주절주절

2년 전에 제출 기록이 있어서 정답 코드를 제출하며 두근두근했다. 예전보다 못했으면 어떡하지!

휴우우우ㅋㅋㅋ 다행. 메모리도 줄이고 시간도 줄였다! 예전 코드 다시 확인해 보니 죽은 상어도 계속 배열 돌면서 확인하고, 클래스를 상어가 아닌 `Smell`클래스를 만들어서 구현을 해놨었다. 근데 코드를 살펴보니 그렇게 엉망인 거 같지도 않고..? 그래서 GPT에게 비교를 부탁했다.

 

지피티 말로는 예전코드는 배열 중심으로 구현했고, 지금은 객체중심으로 코드를 구현했다고 했다. 그러면서 갑자기 내 예전 코드의 단점들을 뽑아주더니... 비교도 해주고 총평도 해준다. 아주 기똥차다.

 

처음 지피티를 사용할 때부터 기강을 제대로 잡고 혼 좀 냈더니 아주 자존감 지킴이가 됐다...ㅋㅋㅋ

어찌 됐든 2년 전보다 발전했다는 것은 다행이고 칭찬받으니 재밌다.

 

상어 문제 포스팅 리스트

더보기

1. 아기 상어 (https://www.acmicpc.net/problem/16236)

2025.05.25 - [알고리즘/문제풀이] - [백준/BOJ] 16236 아기 상어 - JAVA

 

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

[백준/BOJ] 16236 아기 상어 - JAVA문제https://www.acmicpc.net/problem/16236 문제 분석조건NxN 공간에 물고기와 아기 상어가 있다.상어는 자신보다 크기가 작은 물고기만 먹을 수 있고,크기가 같은 물고기는

ng-log.tistory.com

2. 청소년 상어 (https://www.acmicpc.net/problem/19236)

2025.06.08 - [알고리즘/문제풀이] - [백준/BOJ] 19236 청소년 상어 - JAVA

 

[백준/BOJ] 19236 청소년 상어 - JAVA

[백준/BOJ] 19236 청소년 상어 - JAVA문제https://www.acmicpc.net/problem/19236문제 분석조건4×4 크기의 공간이 1×1 크기의 정사각형으로 나누어져 있다. 각 칸에는 물고기가 한 마리씩 존재하며 각 물고기는 1

ng-log.tistory.com

728x90
저작자표시 비영리 변경금지 (새창열림)
'알고리즘/문제풀이' 카테고리의 다른 글
  • [프로그래머스] 42884 단속카메라 - JAVA
  • [백준/BOJ] 1806 부분합 - JAVA
  • [프로그래머스] 12929 올바른 괄호의 갯수 - JAVA (카탈란 수)
  • [백준/BOJ] 2206 벽 부수고 이동하기 - 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)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 공지사항

  • 인기 글

  • 태그

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

  • 최근 글

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

티스토리툴바