[백준][JAVA] 백준 7562번 :: 나이트의 이동 :: 실버 1

Posted by ash tensor on May 02, 2024 · 12 mins read 카테고리
카테고리 링크
📁 JAVA 📁 PS 📁 백준

[백준][JAVA] 백준 7562번 :: 나이트의 이동 :: 실버 1

문제 설명

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

문제 입력

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

각 테스트 케이스는 세 줄로 이루어져 있다. 첫째 줄에는 체스판의 한 변의 길이 l(4 ≤ l ≤ 300)이 주어진다. 체스판의 크기는 l × l이다. 체스판의 각 칸은 두 수의 쌍 {0, …, l-1} × {0, …, l-1}로 나타낼 수 있다. 둘째 줄과 셋째 줄에는 나이트가 현재 있는 칸, 나이트가 이동하려고 하는 칸이 주어진다.

출력

각 테스트 케이스마다 나이트가 최소 몇 번만에 이동할 수 있는지 출력한다.

예제 입력

3

8

0 0

7 0

100

0 0

30 50

10

1 1

1 1

예제 출력

5

28

0

접근방법

오랜만에 푸는 알고리즘 문제였다!! 빅데이터 분석기사(필기), 정보처리기사(실기) 시험을 준비하느라 알고리즘 문제를 풀지 못했는데, 오랜만에 풀어보니까 너무 재밌었다. 다행히 두 시험 다 합격했는데, 이제부터 조금씩이라도 알고리즘 문제를 풀고 싶다.

문제는 그렇게 어렵지 않아서 금방 풀 수 있었고, 살짝 흔한 BFS 문제였다. 나는 알고리즘 문제를 밀리의 서재에 있는 [주요기업/금융권 IT 디지털 직무 채용대비 한권으로 합격하는 취업 코딩테스트] 라는 책을 참고하면서 풀고 있는데, 올해 안에 이 책의 모든 문제를 풀이하고 시간이 허락한다면 PCCP라는 알고리즘 자격증 시험에서 높은 점수를 얻는게 목표다(우선순위는 낮지만).

이 책은 솔직히 엄청 좋냐? 하면 그건 아닌 것 같은데 그렇다고 엄청 나쁘냐 하면 그렇지는 않다. 왜냐하면 알고리즘 문제는 그냥 많이 풀어보는게 답이라고 생각하기 때문이다. 시간을 들인 만큼 정직하게 결과가 나오기 떄문에 그냥 많이 풀어보는게 답이라고 생각한다. 그래서 이 책이 아니라 다른 책을 찾는다고 해도 솔직히 큰 차이가 없을 것 같다.

아무튼 최소거리를 구하라고 하면 BFS 문제이고, 이동한 구체적인 루트를 적으라고 하지는 않았으니 노드 각각 방문한 노드를 개별로 구현할 필요는 없고, 전체 BFS 내에서 방문한 노드를 체크하기만 하면 된다.

코드

나이트 노드 클래스와 메인 클래스를 구현했는데, 지금 보니 KnightNode 클래스 내에 getKnightMoves 메소드를 구현하는 것이 더 좋았을 것 같다. 지금은 메인 클래스 내에 존재하는데, 객체지향적으로 생각했을때 좋은 설계가 아니었다는 생각이 든다.

KnightNode 클래스는 나이트의 현재 위치, 방문한 노드들, 깊이를 저장하는 클래스이고, 나이트가 방문한 노드를 표시하는 int[][] visitedNodes를 가지고 있다. visitedNodes는 나이트 기물에 각각 생성되는 게 아니라, 전역의 visitedNodes를 가리킨다.


package boj;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

class KnightNode {
    int[] coordinate;
    int[][] visitedNodes;
    int depth = 0;
    
    //최초의 나이트 노드 생성용
    
    public KnightNode(int[] coordinate, int boardLength) {
        this.coordinate = coordinate;
        this.visitedNodes = new int[boardLength][boardLength];
        this.visit(coordinate);
    }
    
    // bfs를 돌며 생성되는 나이트 노드 생성용 생성자
    public KnightNode(int[] coordinate, int[][] visitedNodes, int depth) {
        this.coordinate = coordinate;
        
        //new int[][] visitedNodes를 하지 않고 visitedNodes를 그대로 가리키게 하면
        //각각의 나이트 노드가 같은 visitedNodes를 가리키게 되어서
        //나이트 노드 각각의 visitedNodes가 생성되는 것이 아니라
        //전역의 visitedNodes를 가리키게 된다.
        
        this.visitedNodes = visitedNodes;
        this.depth = depth;
    }

    public void visit(int[] coordinate) {
    // 방문한 노드를 표시한다.
        this.visitedNodes[coordinate[0]][coordinate[1]] = 1;
    }
    public boolean checkVisited(int[] coordinate) {
        // 방문했으면 참, 방문하지 않았으면 거짓
        if (this.visitedNodes[coordinate[0]][coordinate[1]] == 1) {
            return true;
        }
        else {
            return false;
        }
    }
}
public class newboj7562 {
    static int testCaseNumber;
    static ArrayList<ArrayList<int[]>> testCases = new ArrayList<>();
    static ArrayList<int[]> movementMetrix = new ArrayList<>();
    static int[][] visitedList;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        inputMethod(scanner);
        
        //나이트가 이동할 수 있는 방향을 저장한다.
        
        movementMetrix.add(new int[] {-2, -1});
        movementMetrix.add(new int[] {-2, 1});
        movementMetrix.add(new int[] {-1, -2});
        movementMetrix.add(new int[] {1, -2});
        movementMetrix.add(new int[] {-1, 2});
        movementMetrix.add(new int[] {+1, 2});
        movementMetrix.add(new int[] {2, -1});
        movementMetrix.add(new int[] {2, 1});

        for(ArrayList<int[]> testCase : testCases) {
            KnightNode answerKnight = bfs(testCase);
            System.out.println(answerKnight.depth);

        }
    }
    public static KnightNode bfs(ArrayList<int[]> testCase) {
        int[] boardLength = testCase.get(0);
        int[] nowPosition = testCase.get(1);
        int[] destination = testCase.get(2);

        visitedList = new int[boardLength[0]][boardLength[0]];

        ArrayDeque<KnightNode> model = new ArrayDeque<>();
        model.addLast(new KnightNode2(nowPosition, boardLength[0]));

        while (model.size() != 0) {
            KnightNode selectedKnight = model.pollFirst();
            if (Arrays.equals(selectedKnight.coordinate, destination)) {
                return selectedKnight;
            }
            else {
                ArrayList<int[]> coordinates = getKnightMoves(boardLength[0],
                        selectedKnight.coordinate);

                for (int[] x : coordinates) {
                    if(selectedKnight.checkVisited(x)) {// 방문했으면 pass
                    }
                    else {
                        KnightNode tempKnight = new KnightNode2(x, 
                         selectedKnight.visitedNodes, selectedKnight.depth + 1);
                        tempKnight.visit(x);
                        model.addLast(tempKnight);
                    }
                }
            }
        }
        return null;
    }
    public static ArrayList<int[]> getKnightMoves(int boardLength, int[] coordinates) {
        ArrayList<int[]> returnArray = new ArrayList<>();
        for(int[] x : movementMetrix) {
            int[] tempCoordinates = new int[2];
            int i = 0;
            for(int y : x) {
                tempCoordinates[i] = coordinates[i++] + y;
            }
            if(checkBoardLimit(boardLength, tempCoordinates)) {
                // BoardLimit내에 위치하면
                returnArray.add(tempCoordinates);
            }
        }
        return returnArray;
    }
    public static boolean checkBoardLimit(int boardLength, int[] coordinate) {
        for (int x : coordinate) {
            if (x < 0) {
                return false;
            }
            else if (x >= boardLength) {
                return false;
            }
        }
        return true;
    }
    public static void inputMethod(Scanner scanner) {
        newboj7562.testCaseNumber = scanner.nextInt();
        scanner.nextLine();
        for (int i = 0; i < newboj7562.testCaseNumber; i++) {
            ArrayList<int[]> testCase = new ArrayList<>();
            int boardLength = scanner.nextInt();
            testCase.add(new int[] {boardLength});
            scanner.nextLine();

            for (int j = 0; j < 2; j++) {
                String coordinates = scanner.nextLine();
                int[] parsedLine = parsingString(coordinates);
                testCase.add(parsedLine);
            }
            testCases.add(testCase);
        }
    }
    public static int[] parsingString(String string) {
        String[] tempString = string.split(" ");
        int[] ints = new int[2];

        int i = 0;
        for (String x : tempString) {
            int temp = Integer.parseInt(x);
            ints[i++] = temp;
        }
        return ints;
    }
}

좀 더 깔끔하게 풀이할 수도 있을 것 같은데, 오랜만에 알고리즘 문제를 푼 것 치고는 감을 잡는데 도움이 된 것 같아서 만족스럽다.


Thanks. mind sharing?

← Previous Post Next Post