본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로컴퓨터 과학 › 알고리즘 › 코딩 테스트

[BaekJoon-7562] 나이트의 이동

목차

나이트의 이동

문제

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

입력

입력의 첫째 줄에는 테스트 케이스의 개수가 주어진다.

각 테스트 케이스는 세 줄로 이루어져 있다. 첫째 줄에는 체스판의 한 변의 길이 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));
                    }
                }
            }
        }
    }
}

백준 원문 문제

같은 카테고리의 글