목차
나이트의 이동
문제
체스판 위에 한 나이트가 놓여져 있다. 나이트가 한 번에 이동할 수 있는 칸은 아래 그림에 나와있다. 나이트가 이동하려고 하는 칸이 주어진다. 나이트는 몇 번 움직이면 이 칸으로 이동할 수 있을까?

입력
입력의 첫째 줄에는 테스트 케이스의 개수가 주어진다.
각 테스트 케이스는 세 줄로 이루어져 있다. 첫째 줄에는 체스판의 한 변의 길이 l(4 ≤ l ≤ 300)이 주어진다. 체스판의 크기는 l × l이다. 체스판의 각 칸은 두 수의 쌍 {0, …, l-1} × {0, …, l-1}로 나타낼 수 있다. 둘째 줄과 셋째 줄에는 나이트가 현재 있는 칸, 나이트가 이동하려고 하는 칸이 주어진다.
출력
각 테스트 케이스마다 나이트가 몇 번만에 이동할 수 있는지 출력한다.
예제 입력 1
3
8
0 0
7 0
100
0 0
30 50
10
1 1
1 1
예제 출력 1
5
28
0
풀이
8칸 이동할 수 있는 나이트의 이동범위를 설정해주고 BFS 를 이용하여 문제를 해결할 수 있다.
private static int[][] PATH = {{1,2}, {2,1}, {-1,2}, {-2,1}, {1,-2}, {2,-1}, {-1,-2}, {-2,-1}};
BFS 코드
public static void bfs(Queue<Edge> queue, int l) {
while (!queue.isEmpty()) {
Edge e = queue.poll();
int x = e.x;
int y = e.y;
for (int k = 0 ; k < 8; k ++) {
int nx = x + PATH[k][0];
int ny = y + PATH[k][1];
if (nx >= 0 && nx < l && ny >=0 && ny < l) {
if (depth[nx][ny] == -1) {
depth[nx][ny] = depth[x][y] + 1;
queue.add(new Edge(nx, ny));
}
}
}
}
}
칸을 그래프의 정점으로 보기
체스판의 각 칸을 정점, 나이트가 한 번에 이동할 수 있는 관계를 간선으로 생각한다. 간선 한 개가 정확히 한 번의 이동이므로 BFS가 목적지에 처음 도달했을 때의 깊이가 최소 이동 횟수다. 8×8 예제의 (0,0)에서 (7,0)으로는 (2,1)→(4,2)→(6,3)→(5,1)→(7,0)처럼 다섯 번에 갈 수 있다. 이 경로 하나만으로 최소임을 증명하는 것은 아니고, BFS의 거리 순서가 최소성을 보장한다.
depth 배열은 처음에 전부 −1로 채워 미방문 표시로 쓴다. 시작 칸은 0으로 바꿔 큐에 넣는다. 각 칸에서 여덟 방향을 시도하고 범위 안이면서 아직 방문하지 않은 칸만 거리+1로 기록한다. 방문 표시를 큐에 넣을 때 하면 같은 칸이 여러 부모에서 중복으로 들어가지 않는다.
테스트 케이스마다 보드를 새로 만들고 거리도 다시 초기화한다. 시작과 도착이 같으면 이미 depth[start]=0이므로 결과는 0이다. 보드 한 변이 L이면 최대 L²개 칸과 각각 8개 후보를 보므로 O(L²) 시간, 거리 배열과 큐에 O(L²) 공간이 든다.
구석 (0,0)에서는 여덟 후보 중 (1,2)와 (2,1)만 판 안에 남는다. 이 두 칸은 거리 1이고, 다음에 이들에서 나아가는 칸은 거리 2다. 나이트가 장애물 없이 움직이는 문제라도 판 밖으로 나갈 수 없으므로 매 후보의 행과 열을 모두 검사해야 한다.
목적지를 발견했을 때 바로 BFS를 중단하는 최적화도 가능하다. 현재 코드는 판 전체의 거리까지 계산한 뒤 목적지 값을 읽는다. L≤300이라 구조가 단순하고, 목적지가 시작점인 사례도 별도 분기 없이 처리한다. 여러 테스트 케이스의 depth를 공유하지 않는 점도 중요하다.
소스코드
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
/**
* 7562 : 나이트의 이동
*/
public class Main {
private static int[][] PATH = {{1,2}, {2,1}, {-1,2}, {-2,1}, {1,-2}, {2,-1}, {-1,-2}, {-2,-1}};
private static int[][] depth;
static class Edge {
int x;
int y;
Edge (int x, int y) {
this.x = x;
this.y = y;
}
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int t = scan.nextInt();
while (t-- != 0) {
int l = scan.nextInt();
depth = new int[l][l];
int sx = scan.nextInt();
int sy = scan.nextInt();
int dx = scan.nextInt();
int dy = scan.nextInt();
for (int i = 0; i < l; i++) {
Arrays.fill(depth[i], -1);
}
Queue<Edge> queue = new LinkedList<>();
queue.offer(new Edge(sx,sy));
depth[sx][sy] = 0; //시작
bfs(queue, l);
System.out.println(depth[dx][dy]);
}
}
public static void bfs(Queue<Edge> queue, int l) {
while (!queue.isEmpty()) {
Edge e = queue.poll();
int x = e.x;
int y = e.y;
for (int k = 0 ; k < 8; k ++) {
int nx = x + PATH[k][0];
int ny = y + PATH[k][1];
if (nx >= 0 && nx < l && ny >=0 && ny < l) {
if (depth[nx][ny] == -1) {
depth[nx][ny] = depth[x][y] + 1;
queue.add(new Edge(nx, ny));
}
}
}
}
}
}