Coding Test/BaekJoon
[백준] 16236번: 아기 상어 - java
찬 주
2023. 8. 10. 11:19
문제
https://www.acmicpc.net/problem/16236
16236번: 아기 상어
N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가
www.acmicpc.net
접근법
최단 거리를 구하는 문제이기 때문에 BFS를 사용한다.
알고리즘
먹을 수 있는 물고기가 남아있을 때까지 BFS를 반복해서 수행한다. 보통은 boolean[][] visited 배열을 사용해 방문 여부를 표시하지만, 나는 물고기를 먹으러 가는데 얼마나 걸리는지를 계산하기 위해 int[][] count를 사용해 해당 지점까지 얼마나 걸리는지를 나타내었다.
BFS를 탐색을 하면서 아기 상어가 먹을 수 있는 물고기를 찾으면 우선 순위 큐에 넣고, 탐색이 끝난 후 우선 순위 큐에서 하나만 꺼내 먹었다는 표시를 해주었다. 여기서 우선 순위 큐는 문제의 조건에서 말한 가장 가까운 물고기를 poll()한다. (만약 한 마리 이상이라면 가장 위에 있는 물고기, 그러한 물고기도 여러마리라면 가장 왼쪽에 있는 물고기) 따라서 Fish 클래스를 만들고, compareTo() 함수를 오버라이딩 해주었다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static int N;
static int answer = 0;
static int[][] map;
static int[][] count;
static int[] dirR = {0, -1, 0, 1};
static int[] dirC = {-1, 0, 1, 0};
static int sharkSize = 2; // 아기 상어 크기
static int eatCount = 0;
static boolean finish = false;
static Deque<Point> queue = new ArrayDeque<>(); // BFS에서 사용할 큐
static PriorityQueue<Fish> pq = new PriorityQueue<>(); // 아기 상어가 먹을 수 있는 물고기들
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
map = new int[N][N];
// input
int edibleFish = 0;
for (int r = 0; r < N; r++) {
StringTokenizer st = new StringTokenizer(br.readLine());
for (int c = 0; c < N; c++) {
map[r][c] = Integer.parseInt(st.nextToken());
if (map[r][c] == 9) {
queue.offer(new Point(r, c));
map[r][c] = 0; // 시작 위치도 빈 칸으로
} else if (map[r][c] == 1) {
edibleFish++;
}
}
}
// 먹을 수 있는 먹이가 없는 경우
if (edibleFish == 0) {
System.out.println(0);
return;
}
// 엄마에게 도움을 요청하기 전까지 반복
while (true) {
start();
if (finish) {
break;
}
}
System.out.println(answer);
}
private static void start() {
count = new int[N][N];
// 시작 위치 설정 (방문 하지 않은 곳 = 0으로 판단하기 위해 시작 점을 1로 해준다)
count[queue.peek().row][queue.peek().column] = 1;
while (!queue.isEmpty()) {
Point now = queue.poll();
for (int i = 0; i < 4; i++) {
int nextR = now.row + dirR[i];
int nextC = now.column + dirC[i];
// 범위를 벗어나거나, 방문한 적이 있거나, map에 적힌 수가 상어의 크기보다 크면 제외
if (!rangeIn(nextR, nextC) || count[nextR][nextC] > 0 || map[nextR][nextC] > sharkSize) {
continue;
}
// 빈 칸이 아니고, 아기 상어 크기보다 작은 물고기인 경우
if (map[nextR][nextC] != 0 && map[nextR][nextC] < sharkSize) {
pq.offer(new Fish(nextR, nextC, count[now.row][now.column]));
}
queue.offer(new Point(nextR, nextC));
count[nextR][nextC] = count[now.row][now.column] + 1;
}
}
// 더 이상 먹을 수 있는 물고기가 없는 경우
if (pq.isEmpty()) {
finish = true;
return;
}
Fish fish = pq.poll(); // 아기 상어가 먹은 물고기
answer += fish.distance; // 그 물고기까지 간 거리를 더해준다.
// 먹은 횟수와 아기 상어의 크기가 같은 경우
if (++eatCount == sharkSize) {
sharkSize++;
eatCount = 0; // 먹은 횟수 초기화
}
map[fish.row][fish.column] = 0; // 물고기를 먹었으므로 빈 칸으로 바꿔준다.
queue.offer(new Point(fish.row, fish.column)); // 다음에 아기 상어가 시작할 위치 = 방금 물고기를 먹은 위치
pq.clear();
}
private static boolean rangeIn(int r, int c) {
return (r >= 0) && (c >= 0) && (r < N) && (c < N);
}
static class Point {
int row, column;
public Point(int row, int column) {
this.row = row;
this.column = column;
}
}
static class Fish implements Comparable<Fish> {
int row, column; // 물고기 위치
int distance; // 상어와 물고기까지의 거리
public Fish(int row, int column, int distance) {
this.row = row;
this.column = column;
this.distance = distance;
}
@Override
public int compareTo(Fish fish) {
if (this.distance == fish.distance) {
if (this.row == fish.row) {
return this.column - fish.column;
}
return this.row - fish.row;
}
return this.distance - fish.distance;
}
}
}