난이도 : 골드 3
유형 : BFS, 구현, 시뮬레이션, 정렬
https://www.acmicpc.net/problem/16236
N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다.
아기 상어와 물고기는 모두 크기를 가지고 있고, 이 크기는 자연수이다. 가장 처음에 아기 상어의 크기는 2이고, 아기 상어는 1초에 상하좌우로 인접한 한 칸씩 이동한다.
아기 상어는 자신의 크기보다 큰 물고기가 있는 칸은 지나갈 수 없고, 나머지 칸은 모두 지나갈 수 있다. 아기 상어는 자신의 크기보다 작은 물고기만 먹을 수 있다. 따라서, 크기가 같은 물고기는 먹을 수 없지만, 그 물고기가 있는 칸은 지나갈 수 있다.
아기 상어가 어디로 이동할지 결정하는 방법은 아래와 같다.
아기 상어의 이동은 1초 걸리고, 물고기를 먹는데 걸리는 시간은 없다고 가정한다. 즉, 아기 상어가 먹을 수 있는 물고기가 있는 칸으로 이동했다면, 이동과 동시에 물고기를 먹는다. 물고기를 먹으면, 그 칸은 빈 칸이 된다.
아기 상어는 자신의 크기와 같은 수의 물고기를 먹을 때 마다 크기가 1 증가한다. 예를 들어, 크기가 2인 아기 상어는 물고기를 2마리 먹으면 크기가 3이 된다.
공간의 상태가 주어졌을 때, 아기 상어가 몇 초 동안 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는지 구하는 프로그램을 작성하시오.
첫째 줄에 공간의 크기 N(2 ≤ N ≤ 20)이 주어진다.
둘째 줄부터 N개의 줄에 공간의 상태가 주어진다. 공간의 상태는 0, 1, 2, 3, 4, 5, 6, 9로 이루어져 있고, 아래와 같은 의미를 가진다.
아기 상어는 공간에 한 마리 있다.
첫째 줄에 아기 상어가 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는 시간을 출력한다.
조건이 까다로워서 구현하기가 까다로웠던 문제였다.
기본적인 로직은 BFS로 아기상어가 이동할 수 있는 칸을 탐색한 후 조건에 맞는 칸으로 이동해주다가 더이상 이동할 칸이 없으면 종료되는 방식으로 코드를 짜면 된다.
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;
/**
* BOJ_16236_G3_아기 상어
* @author mingggkeee
* BFS, 조건분기, 구현
* 0 : 빈칸, {1,2,3,4,5,6} : 물고기의 크기, 9 : 아기 상어의 위치
*/
public class BOJ_16236 {
static int N;
static int time;
static int[][] map;
static int startR, startC;
static int[][] dir = {{0,1}, {0,-1}, {1,0}, {-1,0}};
static int weight = 2; // 초기에는 무게가 2
static int count = 2; // 무게가 증가하기 위해 먹어야하는 물고기 수
static Queue<Location> queue;
static boolean[][] isVisited;
static class Location{
int r;
int c;
int loc;
int weight;
public Location(int r, int c, int loc) {
this.r=r;
this.c=c;
this.loc=loc;
}
}
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
map = new int[N][N];
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) {
map[r][c] = 0;
startR = r;
startC = c;
}
}
}
while(true) {
if(!bfs(startR, startC)) {
break;
}
}
System.out.println(time);
}
static boolean bfs(int r, int c) {
queue = new LinkedList<>();
isVisited = new boolean[N][N];
isVisited[r][c] = true;
List<Location> list = new ArrayList<>();
queue.offer(new Location(r,c,0));
while(!queue.isEmpty()) {
Location now = queue.poll();
for(int i=0; i<4; i++) {
int nr = now.r + dir[i][0];
int nc = now.c + dir[i][1];
if(nr>=0 && nc>=0 && nr<N && nc<N && !isVisited[nr][nc] && weight>=map[nr][nc]) {
isVisited[nr][nc] = true;
queue.offer(new Location(nr,nc,now.loc+1));
if(map[nr][nc] != 0 && map[nr][nc] < weight) {
list.add(new Location(nr,nc,now.loc+1));
}
}
}
}
Collections.sort(list, new Comparator<Location>() {
@Override
public int compare(Location o1, Location o2) {
if(o1.loc==o2.loc) {
if(o1.r==o2.r) {
return o1.c-o2.c;
}
return o1.r-o2.r;
}
return o1.loc-o2.loc;
}
});
if(list.size()==0) {
return false;
}
Location go = list.get(0);
startR = go.r;
startC = go.c;
time += go.loc;
if(map[go.r][go.c]<weight) {
map[go.r][go.c] = 0;
count--;
if(count==0) {
weight++;
count = weight;
}
}
return true;
}
}