티스토리 뷰
https://www.acmicpc.net/problem/30894
30894번: 유령의 집 탈출하기
첫째 줄에 유령의 집의 크기 $N, M$이 주어집니다.$(2≤N,M≤200)$ 둘째 줄에 유령의 집의 입구 좌표 $S_x, S_y$, 출구 좌표 $E_x, E_y$가 주어집니다.$(1≤S_x, E_x≤N, 1≤S_y, E_y≤M)$ 좌표 $(x, y)$는 위에서부터
www.acmicpc.net
문제
오랜만에 놀이공원에 놀러 가기로 한 석준이는 친구들과 다음과 같은 대화를 나누었습니다.
- 친구A: 유령의 집은 너무 무서울 것 같지 않아? 그냥 롤러코스터 타러 가자...
- 석준: 에이~ 유령의 집이 뭐가 무서워? 줄도 없는데 빨리 들어갔다 나오자!
- 친구B: 그래? 그러면 네가 앞장서서 가면 되겠다. 잘 부탁해~
- 석준: ....
사실 석준이는 누구보다도 유령을 무서워하지만, 이미 허세를 부려버려 돌이킬 방법이 없었습니다.
세로 칸, 가로 칸 크기의 유령의 집은 다음과 같이 구성되어 있습니다.
- 빈칸(.): 석준이가 움직일 수 있는 공간을 의미합니다.
- 벽(#): 석준이가 움직일 수 없는 공간을 의미합니다.
- 유령(0, 1, 2, 3): 각 숫자는 유령이 바라보는 초기 방향을 의미합니다. (0 : 오른쪽, 1 : 아래, 2 : 왼쪽, 3 : 위)
어떤 유령이 바라보는 방향에 벽이나 다른 유령이 존재하는 경우, 시야가 가로막혀 그 뒤의 공간은 볼 수 없습니다. 단, 유령의 시야가 가로막히지 않았고 바라보는 방향에 석준이가 있다면, 유령은 거리에 상관없이 석준이를 발견할 수 있습니다. 각 유령은 매초 시계 방향으로 °도씩 회전하며, 회전하는 동안에는 석준이를 볼 수 없습니다.
석준이는 매초 상하좌우로 인접한 빈칸으로 이동하거나 제자리에 머무를 수 있습니다.
놀이공원 아르바이트 경험이 있던 석준이는 유령의 위치와 지도를 모두 알고 있었고, 어떤 유령에게도 발견되지 않고 최대한 빨리 탈출할 계획을 세우려 합니다.
이미 긴장감에 휩싸여 머리가 새하얘진 석준이를 위해, 여러분이 그 방법을 대신 찾아주세요.
유형 : BFS + 탐색 최적화
접근 방식
- 우선 유령은 움직이지 않는다. 그리고 매초 보는 방향에 따라 석준이의 이동이 제한된다.
- 위 문제를 처음 보고 다음과 같은 사고를 한다면 위험할 수 있다.
- 다음 방향을 고려하여 탐색을 진행할 때 근처에 유령의 시야가 잡히는지 '반복문'으로 봐야지 !
- 위 방법으로 문제를 푼다면 무조건 시간 초과이다. 그리고 굳이 이럴 필요가 없다는 것을 말하고 싶다.
- 유령의 시야는 4가지 방향을 가지고 있다. (0 - > 1 -> 2 -> 3) 그 다음은 ? 다시 0이다. 즉 순환이라는 것이다.
- 좌표계에 따라 유령이 보는 시야는 총 4가지 종류가 있다는 것이다.
- 미리 유령의 시야를 boolean 배열로 만들어 놓는다면 굳이 매번 탐색시 또 반복문을 돌리지 않아도 된다.
- 다른 말로 4초마다 유령의 시야가 다시 돌아온다는 것이다.
- 마지막으로 방문 배열에 대해 얘기 하겠다.
- BFS를 진행할 때 방문 배열을 [n][m][4]로 지정하여 돌아오는 시야마다 해당 위치에 이미 방문한 적이 있는 지 확인한다.
makeSight
static void makeSight(Queue<Node> q){
int nx=0,ny=0;
int idx = 0;
// 방향이 총 4개이므로 4번 반복문으로 시야 배열 초기화
for(int i=0;i<=3;i++){
int size = q.size();
while(size != 0){
Node cur = q.poll();
size -= 1;
sight[cur.x][cur.y][i] = true; // 유령의 위치는 항상 이동 불가능한 고정 위치이다.
idx= 1;
while(true){
nx = cur.x + (idx * dx[cur.d]);
ny = cur.y + (idx * dy[cur.d]); // 전방이 뚫려 있는 한 끝까지 볼 수 있다.
if(nx < 1 || nx > n || ny < 1 || ny > m)
break; // 좌표계 바깥으로 나간 경우
if(board[nx][ny] =='#')
break; // 벽
if(board[nx][ny] >= '0' && board[nx][ny] <= '3')
break; // 다른 유령
sight[nx][ny][i] = true; // 해당 좌표는 유령의 시야에 걸린다.
idx += 1;
}
q.add(new Node(cur.x,cur.y,changeDir(cur.d))); // 해당 유령이 다음 방향으로 볼때를 위해
}
}
}
// 방향 전환 메서드 (0 -> 1 -> 2 -> 3 -> 0) 순환
static int changeDir(int cur){
if(cur == 3)
return 0;
return cur + 1;
}
전체 코드
import java.util.*;
import java.io.*;
public class Main {
static int n = 0;
static int m = 0;
static int sx = 0,sy = 0,ex = 0,ey = 0;
static int[] dx = {0,1,0,-1,0};
static int[] dy = {1,0,-1,0,0};
static char[][] board;
static boolean[][][] sight;
// 0 : 오른쪽, 1 : 아래, 2 : 왼쪽, 3 : 위
public static void main(String[] args) throws Exception {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(bf.readLine(), " ");
n = Integer.parseInt(st.nextToken());
m = Integer.parseInt(st.nextToken());
st = new StringTokenizer(bf.readLine(), " ");
sx = Integer.parseInt(st.nextToken());
sy = Integer.parseInt(st.nextToken());
ex = Integer.parseInt(st.nextToken());
ey = Integer.parseInt(st.nextToken());
board = new char[n+1][m+1];
sight = new boolean[n+1][m+1][4];
Queue<Node> ghost = new LinkedList<>();
for(int i=1;i<=n;i++){
String line = bf.readLine();
for(int j=1;j<=m;j++){
board[i][j] = line.charAt(j-1);
if(board[i][j] >= '0' && board[i][j] <= '3'){
ghost.add(new Node(i,j,board[i][j] - '0'));
}
}
}
makeSight(ghost);
search();
bf.close();
}
static void search(){
Queue<Node> q = new LinkedList<>();
int[][][] v = new int[n+1][m+1][4];
q.add(new Node(sx,sy,0));
v[sx][sy][0] = 1;
while(!q.isEmpty()){
Node cur = q.poll();
if(cur.x == ex && cur.y == ey){
System.out.println(cur.d);
return;
}
for(int i=0;i<5;i++){
int nx = cur.x + dx[i];
int ny = cur.y + dy[i];
int curSight = (cur.d + 1) % 4 ;
if(nx < 1 || nx > n || ny < 1 || ny > m)
continue; // 바깥으로 나간 경우
if(board[nx][ny] == '#')
continue; // 벽인 경우
if(sight[nx][ny][curSight])
continue; // 시야에 잡히는 경우
if(v[nx][ny][curSight] > 0){
continue; // 이미 더 적은 시간에 해당 좌표를 방문한 적 있는 경우
}
v[nx][ny][curSight] = v[cur.x][cur.y][cur.d % 4] + 1;
q.add(new Node(nx,ny,cur.d + 1));
}
}
System.out.println("GG");
}
static void makeSight(Queue<Node> q){
int nx=0,ny=0;
int idx = 0;
// 방향이 총 4개이므로 4번 반복문으로 시야 배열 초기화
for(int i=0;i<=3;i++){
int size = q.size();
while(size != 0){
Node cur = q.poll();
size -= 1;
sight[cur.x][cur.y][i] = true; // 유령의 위치는 항상 이동 불가능한 고정 위치이다.
idx= 1;
while(true){
nx = cur.x + (idx * dx[cur.d]);
ny = cur.y + (idx * dy[cur.d]); // 전방이 뚫려 있는 한 끝까지 볼 수 있다.
if(nx < 1 || nx > n || ny < 1 || ny > m)
break; // 좌표계 바깥으로 나간 경우
if(board[nx][ny] =='#')
break; // 벽
if(board[nx][ny] >= '0' && board[nx][ny] <= '3')
break; // 다른 유령
sight[nx][ny][i] = true; // 해당 좌표는 유령의 시야에 걸린다.
idx += 1;
}
q.add(new Node(cur.x,cur.y,changeDir(cur.d))); // 해당 유령이 다음 방향으로 볼때를 위해
}
}
}
// 방향 전환 메서드 (0 -> 1 -> 2 -> 3 -> 0) 순환
static int changeDir(int cur){
if(cur == 3)
return 0;
return cur + 1;
}
}
class Node{
int x;
int y;
int d;
Node(int x,int y,int d){
this.x = x;
this.y = y;
this.d = d;
}
}
'알고리즘 > 백준' 카테고리의 다른 글
백준 1167 (트리의 지름) - java (0) | 2024.01.15 |
---|---|
백준 2450 (모양 정돈) - java (0) | 2024.01.15 |
백준 1884 (고속도로) - java (1) | 2024.01.11 |
백준 2637 (장남감 조립) - java (1) | 2024.01.10 |
백준 15898 (피아의 아틀리에 ~신비한 대회의 연금술사) - java (0) | 2024.01.10 |
- Total
- Today
- Yesterday
- 백준 #15686 #치킨 배달
- 백준 #13549 #숨바꼭질3
- 백준 #14863 #서울에서 경산까지 #java #자바
- 백준 #18405 #경쟁적 전염
- 17394
- 백준 #
- 백준 #25603 #짱해커 이동식 #java #자바
- 백준 #1987 #알파벳 #자바 #java
- 백준 #17940 #주식 #자바 #java
- 백준 #1325 #효율적인 해킹
- 백준 #2636 #치즈
- 백준 #1759 #암호 만들기
- 백준 #16973 #직사각형 탈출
- 자바
- 백준 #인구 이동 #16234
- 백준 #다리 만들기 #2146
- 백준 #3980 #선발 명단
- 백준 #1584 #게임 #java #자바
- 백준 #치즈 #2638
- 백준 #25195 #yes or yes #java #자바
- 백준 #1727 #커플 만들기 #자바 #java
- 백준 #12014 #주식 #자바 #java
- 백준 #4963 #섬의 개수
- 백준 #2580 #스도쿠
- 백준 #5721 #사탕 줍기 대회 #java #자바
- 백준 #28140 #빨강~ 빨강~ 파랑! 파랑! 달콤한 솜사탕! #java #자바
- 백준
- Java
- 17218
- 자바 #JAVA
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |