백준 1938번 - 통나무 옮기기

황제연·2025년 1월 24일
0

알고리즘

목록 보기
163/169
post-thumbnail

문제 탐색하기

입력 자료 정리

  1. N은 평지의 가로 세로 길이다
  2. 이어서 들어오는 값은 문자열을 포함한 값으로 N X N의 입력값이다
  • 0은 빈칸, 1은 다른 나무, B는 통나무 E는 종료지점이다
  • 0은 이동할 수 있으며 1은 이동할 수 없다
  • B와 E는 같은방향으로 3번 연속되어 나타난다

해결방법 추론

  1. 입력 최대 크기도 작고 가능한 최소 동작횟수를 구하는 문제이므로 bfs탐색에 주어진 조건에 맞춰서 해결하는 구현문제라고 생각했다
  2. 통나무를 잘 관리해서 BFS탐색으로 종료지점에 딱 맞게 움직인다면 정답을 찾을 수 있을 것이다
  3. 다만 통나무가 3칸이라서 조금 관리가 까다롭다. 따라서 중간 위치만 보관하고, 나머지는 상하좌우 이동할 때나 회전할때, 그리고 종료지점 체크할 때 중간지점을 기준으로 그 양옆을 확인하기로 결정했다
  4. 이어서 방향도 기억하고 있어야한다. 통나무의 방향은 가로방향과 세로방향만 가능하다
  5. 이 두가지를 잘 기억하고 실수없이 구현한다면 정답을 구할 수 있을 것이다

시간복잡도 계산

  1. 시간복잡도는 BFS 탐색에서 정점 N^2과 상수의 간선을 탐색하므로
    O(N^2)이 된다
  2. 따라서 시간제한안에 문제를 해결할 수 있다

코드 설계하기

입력값 상태 관리

  1. n은 int형 변수에 관리한다
  2. arr배열은 char형 2차원 배열로 선언하며 크기는 n x n이다
  3. 통나무가 있는 지점과 종료지점을 구해야한다. 2차원 배열도 좋지만 이번에는 Pos라는 별도의 클래스를 만들었다. 이번에도 x와 y를 뒤집었다!
  4. 두 지점 모두 3칸 좌표를 가지므로 크기를 3으로 하는 Pos타입의 배열로 선언했다
  5. B일때는 시작지점에 E일 때는 종료지점에 좌표를 넣어준다
  6. 또한 4방 탐색 편의 배열도 BFS 탐색을 위해 선언한다
  7. bfs 탐색 결과를 ans에 담는다.

BFS 구현 설계

  1. 방문배열을 3차원으로 선언한다. 2xnxn 크기로 방향-x-y값 순이다
  2. 현재 방향을 파악하기 위해 시작지점의 0번 인덱스의 y+1이 1번 인덱스의 1과 같은지 확인한다
  3. 만약 같다면 가로 방향으로 있는 것이고 0으로 설정한다. 아니라면 세로방향이므로 1로 설정한다
  4. 시작지점을 넣고 탐색을 시작한다

정답 조건일 경우

  1. 만약 현재 지점이 종료지점의 중간 지점 좌표와 같다면 방향에 따라 x와 y에 -1, +1한 값을 비교한다
  2. 두 지점 모두 E라면 지금까지의 거리 탐색 결과를 리턴한다

상하좌우 이동 범위 탐색

  1. 상하좌우 탐색을 하며, 방문여부와 범위를 벗어나는지 여부를 확인한다
  2. 만약 모두 통과하면 거리 + 1 한 결과와함께 넣어준다
  3. 이동범위는 방향에 따라 한번 구분하고, 또 현재 탐색한 방향에 따라 또 구분한다
    0,1은 상하 / 1,2는 좌우다.
  4. 가로방향의 통나무일 때, 수평으로 어떻게 이동하는지와 세로방향의 통나무일 때, 수직방향으로 어떻게 이동하는지 생각해서 범위를 벗어나는 경우 false를 리턴한다
  5. 모두 통과하면 true를 리턴한다

회전여부 탐색

  1. 회전 여부는 현재 지점으로부터 3x3범위내에 범위를 벗어나거나 1이 있는지 확인하면 된다
  2. 만약 하나라도 있다면 false를 리턴하고 아니라면 true를 리턴한다
  3. 회전이 가능하다면 다시 방향과 미방문 여부를 체크해서 구분한다
  4. 구분한 결과에 따라 현재 방향의 반대 방향으로 넣어주고 거리+1해서 넣어준다.

출력값 설계

  1. bfs결과를 담은 ans를 출력하면 정답이 된다

정답 코드

import java.io.*;
import java.util.*;


class Pos{
    int x;
    int y;

    public Pos(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Log{
    int x;
    int y;
    int dir;
    int dist;

    public Log(int x, int y, int dir, int dist) {
        this.x = x;
        this.y = y;
        this.dir = dir;
        this.dist = dist;
    }
}


public class Main {

    static int n;
    static char[][] arr;
    static Pos[] start;
    static Pos[] end;
    static int[] dx = {-1,1,0,0};
    static int[] dy = {0,0,-1,1};
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        n = Integer.parseInt(br.readLine());
        arr = new char[n][n];
        start = new Pos[3];
        end = new Pos[3];
        int idx1 = 0;
        int idx2 = 0;
        for (int i = 0; i < n; i++) {
            String s = br.readLine();
            for (int j = 0; j < n; j++) {
                arr[i][j] = s.charAt(j);
                if(arr[i][j] == 'B'){
                    start[idx1++] = new Pos(i, j);
                }
                if(arr[i][j] == 'E'){
                    end[idx2++] = new Pos(i, j);
                }
            }
        }
        int ans = bfs();

        bw.write(ans+"");

        br.close();
        bw.close();
    }

    private static int bfs() {
        boolean[][][] visited = new boolean[2][n][n];
        Queue<Log> q = new LinkedList<>();
        int nowDir = -1;
        if(start[0].y + 1 == start[1].y){
            nowDir = 0;
        }else{
            nowDir = 1;
        }
        q.add(new Log(start[1].x, start[1].y, nowDir, 0));
        visited[nowDir][start[1].x][start[1].y] = true;
        while(!q.isEmpty()){
            Log now = q.poll();

            if(now.x == end[1].x && now.y == end[1].y){
                if(now.dir == 0 && arr[now.x][now.y-1] == 'E' && arr[now.x][now.y+1] == 'E'){
                    return now.dist;
                }else if(now.dir == 1 && arr[now.x-1][now.y] == 'E' && arr[now.x+1][now.y] == 'E'){
                    return now.dist;
                }
            }

            for (int i = 0; i < 4; i++) {
                int nx = now.x + dx[i];
                int ny = now.y + dy[i];
                if(!isRange(nx,ny,now.dir, i)){
                    continue;
                }
                if(visited[now.dir][nx][ny]){
                    continue;
                }

                visited[now.dir][nx][ny] = true;
                q.add(new Log(nx, ny, now.dir, now.dist+1));
            }

            if(canRotate(now.x, now.y)){
                if(now.dir == 0 && !visited[1][now.x][now.y]){
                    visited[1][now.x][now.y] = true;
                    q.add(new Log(now.x, now.y, 1, now.dist+1));
                }else if(now.dir == 1 && !visited[0][now.x][now.y]){
                    visited[0][now.x][now.y] = true;
                    q.add(new Log(now.x, now.y, 0, now.dist+1));
                }
            }

        }
        return 0;
    }

    private static boolean canRotate(int x, int y) {
        for (int i = x-1; i <= x + 1; i++) {
            for (int j = y-1; j <= y + 1; j++) {
                if(i < 0 || i >= n || j < 0 || j >= n){
                    return false;
                }
                if(arr[i][j] == '1'){
                    return false;
                }
            }
        }

        return true;
    }


    private static boolean isRange(int x, int y, int dir, int i) {
        switch(dir) {
            case 0:
                if(i < 2) {
                    if(x < 0 || x >= n){
                        return false;
                    }
                    if(arr[x][y] == '1' || arr[x][y - 1] == '1' || arr[x][y + 1] == '1'){
                        return false;
                    }
                }
                else {
                    if(y - 1 < 0 || y + 1 >= n){
                        return false;
                    }
                    if(arr[x][y] == '1' || arr[x][y - 1] == '1' || arr[x][y + 1] == '1'){
                        return false;
                    }
                }
                break;
            case 1:
                if(i < 2) {
                    if(x - 1 < 0 || x + 1 >= n){
                        return false;
                    }
                    if(arr[x][y] == '1' || arr[x - 1][y] == '1' || arr[x + 1][y] == '1'){
                        return false;
                    }
                }
                else {
                    if(y < 0 || y >= n){
                        return false;
                    }
                    if(arr[x][y] == '1' || arr[x - 1][y] == '1' || arr[x + 1][y] == '1'){
                        return false;
                    }
                }

                break;

        }

        return true;
    }
}

문제 링크

1938번 - 통나무 옮기기

profile
Software Developer

0개의 댓글