맵에서 0은 이동할 수 있는 곳, 1은 이동할 수 없는 곳(벽이 있는 곳)을 나타낸다.
(1,1)에서 (N,M)까지 이동하려는데 (실제 배열에서는 0,0에서 N-1,M-1) 최단경로 구하기
한개의 벽을 부수는 것으로 경로가 짧아진다면 한개까지 부술 수 있음
벽을 하나씩 부수면 시간초과됨!
벽을 부수고 탐색하는 경우와 벽을 부수지 않고 탐색하는 경우를 나타내는 배열을 둔다.
(3차원 배열을 사용한다.)
import java.util.*;
import java.io.*;
class Wall {
    int x, y; //현재 map의 위치
    boolean change; //지금까지 벽을 부순적 있는지
    Wall(int x, int y, boolean change) {
        this.x = x;
        this.y = y;
        this.change = change;
    }
}
public class Main {
    static int N, M, r_min = Integer.MAX_VALUE, min = Integer.MAX_VALUE;
    static int[][] map;
    static int[][][] dist; //거리를 나타냄
    static boolean[][][] visited; 
    static boolean flag ;
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        map = new int[N][M];
        dist = new int[N][M][2];
        visited = new boolean[N][M][2];
        int count = 0;
        for(int i=0; i<N; i++) {
            String str = br.readLine();
            for(int j=0; j<M; j++) {
                map[i][j] = Integer.parseInt(Character.toString(str.charAt(j)));
            }
        }
        bfs(0, 0); 
        
    }
    static void bfs(int x, int y) {
    
        Queue<Wall> queue = new LinkedList<>();
        int[] dx = {-1, 0, 1, 0};
        int[] dy = {0, -1, 0, 1};
        boolean flag = false;
        if(map[x][y] == 0){ //벽이 아니면
            visited[x][y][0] = true;
            queue.add(new Wall(x, y, false));
        } else {  
            visited[x][y][1] = true;
            queue.add(new Wall(x, y, true)); 
        }
        while(!queue.isEmpty()) {
            Wall out = queue.poll();
            if(out.x == N-1 && out.y == M-1){
                if(out.change)
                    System.out.println(dist[out.x][out.y][1] + 1);
                else
                    System.out.println(dist[out.x][out.y][0] + 1);
               
                flag = true;
                return ;
            }
            for(int i=0; i<4; i++) {
                int nx = out.x + dx[i];
                int ny = out.y + dy[i];
                if(nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
                else if(map[nx][ny] == 0) { //벽이 아닐 때
                    if(!out.change && !visited[nx][ny][0]) { //벽을 부순적이 없다면
                        visited[nx][ny][0] = true;
                        queue.add(new Wall(nx, ny, out.change));
                        dist[nx][ny][0] = dist[out.x][out.y][0] + 1;
                    } 
                    if(out.change && !visited[nx][ny][1]) { //벽을 부순적이 있다면
                        visited[nx][ny][1] = true;
                        queue.add(new Wall(nx, ny, out.change));
                        dist[nx][ny][1] = dist[out.x][out.y][1] + 1;
                    }
                } 
                else if(map[nx][ny] == 1) { //벽일 떄
                    if(!out.change && !visited[nx][ny][0]) { //벽을 부순적이 없으면
                        visited[nx][ny][1] = true;
                        queue.add(new Wall(nx, ny, true));
                        dist[nx][ny][1] = dist[out.x][out.y][0] + 1;
                    }
                }
            }
        }
        if(!flag)
            System.out.println(-1);
    }
    
}