1. 문제
https://www.acmicpc.net/problem/1600
동물원에서 막 탈출한 원숭이 한 마리가 세상구경을 하고 있다. 그 녀석은 말(Horse)이 되기를 간절히 원했다. 그래서 그는 말의 움직임을 유심히 살펴보고 그대로 따라 하기로 하였다. 말은 말이다. 말은 격자판에서 체스의 나이트와 같은 이동방식을 가진다. 다음 그림에 말의 이동방법이 나타나있다. x표시한 곳으로 말이 갈 수 있다는 뜻이다. 참고로 말은 장애물을 뛰어넘을 수 있다.
x | x | |||
x | x | |||
말 | ||||
x | x | |||
x | x |
근데 원숭이는 한 가지 착각하고 있는 것이 있다. 말은 저렇게 움직일 수 있지만 원숭이는 능력이 부족해서 총 K번만 위와 같이 움직일 수 있고, 그 외에는 그냥 인접한 칸으로만 움직일 수 있다. 대각선 방향은 인접한 칸에 포함되지 않는다.
이제 원숭이는 머나먼 여행길을 떠난다. 격자판의 맨 왼쪽 위에서 시작해서 맨 오른쪽 아래까지 가야한다. 인접한 네 방향으로 한 번 움직이는 것, 말의 움직임으로 한 번 움직이는 것, 모두 한 번의 동작으로 친다. 격자판이 주어졌을 때, 원숭이가 최소한의 동작으로 시작지점에서 도착지점까지 갈 수 있는 방법을 알아내는 프로그램을 작성하시오.
2. 풀이
- 몇번이나 말처럼 움직이는지 체크하기 위해 방문배열을 3차원으로 만들었다. (visited[][][]) 마지막을 K+1칸으로 한 이유는 0번부터 K번까지 움직일 수 있기 때문이다.
- 그 외에는 BFS로 평범하게 풀었다.
- 코드에 주석 친 부분을 해제하고 36~39줄을 없앤다면 99퍼센트에서 틀렸다는 결과가 나온다. 큐에 다음 값을 넣으면서 체크하면 다음 값이 없는 경우는 확인할 수 없다. 예를 들어서 이런 반례가 있을 수 있다. 이 경우는 0이 나와야 하는데 -1이 출력된다.
1
1 1
0
import java.io.*;
import java.util.*;
public class BOJ_1600_말이되고픈원숭이 {
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int K = Integer.parseInt(in.readLine());
StringTokenizer st = new StringTokenizer(in.readLine());
int C = Integer.parseInt(st.nextToken());
int R = Integer.parseInt(st.nextToken());
int[][] board = new int[R][C];
for (int i = 0; i < R; i++) {
st = new StringTokenizer(in.readLine());
for (int j = 0; j < C; j++) {
board[i][j] = Integer.parseInt(st.nextToken());
}
}
int[][] deltas = {{-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2},
{2, -1}, {2, 1}, {1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int[] now = {0, 0, 0};
int[][][] visited = new int[R][C][K+1];
Queue<int[]> queue = new ArrayDeque<>();
queue.offer(now);
visited[0][0][0] = 1;
while (!queue.isEmpty()) {
now = queue.poll();
int r = now[0];
int c = now[1];
int k = now[2];
int len = visited[r][c][k];
if(r==R-1 && c==C-1){
System.out.println(len-1);
return;
}
for (int d = 0; d < 12; d++) {
int nr = r + deltas[d][0];
int nc = c + deltas[d][1];
int nk = d<8? k+1:k;
if (nr < 0 || nr >= R || nc < 0 || nc >= C || board[nr][nc] == 1) continue;
else if(d<8 && nk>K) continue;
else if(visited[nr][nc][nk]!=0) continue;
queue.offer(new int[]{nr,nc,nk});
visited[nr][nc][nk] = len+1;
// if(nr==R-1 && nc==C-1){
// System.out.println(len);
// return;
// }
}
}
System.out.println(-1);
}
}
3. 결과
큐에 2차원 배열 넣고 하다가 여러번 틀리고 3차원 배열 사용할때도 리턴 조건 설정을 다음 수 집어넣으면서 했다가(본문 코드의 주석 부분) 여러번 틀렸다. 리턴조건 설정 얘기는 풀이하면서 했으니 넘어간다.
'코딩테스트 > BOJ' 카테고리의 다른 글
[BOJ] 20040 사이클 게임 - JAVA (0) | 2022.04.03 |
---|---|
[BOJ] 4195 친구 네트워크 - JAVA (0) | 2022.04.03 |
[BOJ] 9020 골드바흐의 추측 - JAVA (0) | 2022.04.01 |
[BOJ] 1976 여행 가자 - JAVA (0) | 2022.04.01 |
[BOJ] 1717 집합의 표현 - JAVA (0) | 2022.03.31 |
댓글