문제
사악한 암흑의 군주 이민혁은 드디어 마법 구슬을 손에 넣었고, 그 능력을 실험해보기 위해 근처의 티떱숲에 홍수를 일으키려고 한다. 이 숲에는 고슴도치가 한 마리 살고 있다. 고슴도치는 제일 친한 친구인 비버의 굴로 가능한 빨리 도망가 홍수를 피하려고 한다.
티떱숲의 지도는 R행 C열로 이루어져 있다. 비어있는 곳은 '.'로 표시되어 있고, 물이 차있는 지역은 '*', 돌은 'X'로 표시되어 있다. 비버의 굴은 'D'로, 고슴도치의 위치는 'S'로 나타내어져 있다.
매 분마다 고슴도치는 현재 있는 칸과 인접한 네 칸 중 하나로 이동할 수 있다. (위, 아래, 오른쪽, 왼쪽) 물도 매 분마다 비어있는 칸으로 확장한다. 물이 있는 칸과 인접해있는 비어있는 칸(적어도 한 변을 공유)은 물이 차게 된다. 물과 고슴도치는 돌을 통과할 수 없다. 또, 고슴도치는 물로 차있는 구역으로 이동할 수 없고, 물도 비버의 소굴로 이동할 수 없다.
티떱숲의 지도가 주어졌을 때, 고슴도치가 안전하게 비버의 굴로 이동하기 위해 필요한 최소 시간을 구하는 프로그램을 작성하시오.
고슴도치는 물이 찰 예정인 칸으로 이동할 수 없다. 즉, 다음 시간에 물이 찰 예정인 칸으로 고슴도치는 이동할 수 없다. 이동할 수 있으면 고슴도치가 물에 빠지기 때문이다.
입력
첫째 줄에 50보다 작거나 같은 자연수 R과 C가 주어진다.
다음 R개 줄에는 티떱숲의 지도가 주어지며, 문제에서 설명한 문자만 주어진다. 'D'와 'S'는 하나씩만 주어진다.
출력
첫째 줄에 고슴도치가 비버의 굴로 이동할 수 있는 가장 빠른 시간을 출력한다. 만약, 안전하게 비버의 굴로 이동할 수 없다면, "KAKTUS"를 출력한다.
예제 입력 1
3 3
D.*
...
.S.
예제 출력 1
3
예제 입력 2
3 3
D.*
...
..S
예제 출력 2
KAKTUS
예제 입력 3
3 6
D...*.
.X.X..
....S.
예제 출력 3
6
예제 입력 4
5 4
.D.*
....
..X.
S.*.
....
예제 출력 4
4
소스코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
private static BufferedReader br;
private static StringTokenizer st;
private static int R, C;
private static char [][]map;
private static int []dx= {-1,1,0,0}; // 상, 하, 좌, 우
private static int []dy= {0,0,-1,1};
private static Point hedgehog; // 고슴도치의 상태 저장
private static List<Point> list; // 물의 확장 정보를 저장
static class Point { // 맵에서 (r, c) 좌표, 이동한 시간, 현재 맵의 상태 정보를 저장하기 위한 클래스
int r, c, time;
char [][]map;
public Point(int r, int c, int time, char[][] map) {
super();
this.r = r;
this.c = c;
this.time = time;
this.map = map;
}
@Override
public String toString() {
return "Point [r=" + r + ", c=" + c + ", time=" + time + ", map=" + Arrays.toString(map) + "]";
}
}
public static void main(String[] args) throws IOException {
br=new BufferedReader(new InputStreamReader(System.in));
st=new StringTokenizer(br.readLine());
R=Integer.parseInt(st.nextToken());
C=Integer.parseInt(st.nextToken());
map=new char[R][C];
for(int r=0;r<R;r++) {
String s=br.readLine();
for(int c=0;c<C;c++) {
map[r][c]=s.charAt(c);
if(map[r][c]=='S') hedgehog=new Point(r, c, 0, new char[R][C]); // 고슴도치 정보 저장
}
}
for(int r=0;r<R;r++) {
for(int c=0;c<C;c++) hedgehog.map[r][c]=map[r][c]; // 현재 고슴도치 맵 정보를 복사함
}
int result=bfs();
if(result==-1) System.out.println("KAKTUS");
else System.out.println(result);
}
private static int bfs() { // bfs를 돌며 고슴도치가 비버 굴로 이동하기 위한 최소 시간을 탐색함
Queue<Point> queue=new LinkedList<>(); // 큐에 고슴도치 정보를 저장
queue.add(hedgehog);
boolean [][]visited=new boolean[R][C]; // 고슴도치가 맵에서 (r, c) 좌표를 방문했는지 여부를 저장
visited[hedgehog.r][hedgehog.c]=true;
while(!queue.isEmpty()) {
Point p=queue.poll();
list=new LinkedList<>();
WaterSpread(p.map); // 물 확장
for(int i=0;i<4;i++) { // 고슴도치의 이동 가능한 사방탐색
char [][]tmp=new char[R][C];
copyMap(p.map, tmp); // i번째 방향으로 이동할 경우 맵 정보를 저장하기 위한 새로운 맵
tmp[p.r][p.c]='.'; // 현재 고슴도치 위치는 '.'으로 변경
int nx=p.r+dx[i];
int ny=p.c+dy[i];
if(isIn(nx, ny) && !visited[nx][ny]) { // 고슴도치가 이동할 경로가 맵 범위 내에 있고, 아직 방문하지 않은 점이면 탐색 수행
if(tmp[nx][ny]=='.') { // 이동 가능한 경로이면, 방문 처리 및 고슴도치 위치 설정 후 큐에 넣어줌
visited[nx][ny]=true;
tmp[nx][ny]='S';
queue.add(new Point(nx, ny, p.time+1, tmp));
} else if(tmp[nx][ny]=='D') return p.time+1; // 비버 굴에 도달한 경우, 해당 시간 리턴
}
}
}
return -1; // 비버 굴에 도달할 수 없는 경우
}
private static void WaterSpread(char [][]map) { // 물 확장
for(int r=0;r<R;r++) {
for(int c=0;c<C;c++) {
if(map[r][c]=='*') {
for(int i=0;i<4;i++) {
int nx=r+dx[i];
int ny=c+dy[i];
if(isIn(nx, ny) && map[nx][ny]=='.') {
list.add(new Point(nx, ny, 0, null)); // 물이 확장할 경로를 list에 저장해둠
}
}
}
}
}
for(Point p:list) { // 물 확장 수행
map[p.r][p.c]='*';
}
}
private static void copyMap(char[][] map, char[][]tmp) { // 맵 복사
for(int r=0;r<R;r++) {
for(int c=0;c<C;c++) tmp[r][c]=map[r][c];
}
}
private static boolean isIn(int r, int c) {
return r>=0 && r<R && c>=0 && c<C;
}
}
처음에 visited를 사용해주지 않아서 메모리 초과가 났었습니다. 방문 처리 없이 구현하게 되면, 고슴도치가 이전에 이미 다녀간 곳을 계속해서 큐에 넣어주게 되므로 메모리를 많이 차지하게 되어 메모리 초과가 나게 됩니다. 따라서 고슴도치가 맵에서 (r, c) 좌표를 방문한 경우, 물의 확장 정보와는 상관없이 해당 좌표를 다시 밟지 않도록 방문 처리를 해주어야 합니다.
1. 맵에서의 좌표, 이동한 시간, 이동 경로에 따른 맵의 상태 -> 해당 정보들을 관리해 줄 Point 클래스를 만듧니다.
2. 물 확장 메서드, 맵 복사 메서드, 맵 범위 체크 메서드를 만듧니다.
3. bfs를 이용하여 고슴도치가 비버 굴까지 도달하는 최소 시간을 탐색합니다.
3-1. 큐에 Point를 넣어주고, 시작점 방문 처리를 합니다.
3-2. 물 확장을 먼저 수행합니다.
3-3. 사방탐색을 통해 고슴도치가 이동 가능한 경로를 찾습니다.
3-4. 이동 가능한 경로를 찾으면, 맵 복사를 하고 고슴도치 이동을 수행하여 맵 정보를 업데이트 합니다.
3-5. 비버 굴에 도달하면, 이동하는데 걸린 시간을 리턴합니다.
4. 탐색한 시간이 -1이면 "KAKTUS", 아니면 해당 시간을 출력합니다.
'코딩테스트 > 그래프 탐색' 카테고리의 다른 글
[Java] 백준 1963번 : 소수 경로 (0) | 2022.06.27 |
---|---|
[Java] 백준 10159번 : 저울 (0) | 2022.05.22 |
[Java] 백준 20058번 : 마법사 상어와 파이어스톰 (0) | 2022.05.16 |
[Java] 백준 17141번 : 연구소 2 (0) | 2022.05.15 |
[Java] 백준 13913번 : 숨바꼭질 4 (0) | 2022.05.14 |