(Java) 백준 14940번 - 쉬운 최단거리

코딩너구리·2026년 4월 22일

코딩 문제 풀이

목록 보기
256/266

https://www.acmicpc.net/problem/14940

문제

> 지도가 주어지면 모든 지점에 대해서 목표지점까지의 거리를 구하여라.

> 문제를 쉽게 만들기 위해 오직 가로와 세로로만 움직일 수 있다고 하자.

> 지도의 크기 n과 m이 주어진다.
> n은 세로의 크기, m은 가로의 크기다.
(2 ≤ n ≤ 1000, 2 ≤ m ≤ 1000)

> 다음 n개의 줄에 m개의 숫자가 주어진다.
> 0은 갈 수 없는 땅이고 1은 갈 수 있는 땅, 2는 목표지점이다.
> 입력에서 2는 단 한개이다.

> 각 지점에서 목표지점까지의 거리를 출력한다.
> 원래 갈 수 없는 땅인 위치는 0을 출력하고, 원래 갈 수 있는 땅인 부분 중에서 도달할 수 없는 위치는 -1을 출력한다.

접근

2가 목표 지점이고 모든 점에서 2까지 가야한다. 즉, 1인 모든 지점을 돌면서 그래프 탐색을 해야한다.
하지만 이를 반대로생각해보면 2에서 BFS를 통해 탐색할 수 있는 모든 지점을 다 탐색하고, 해당 지점까지의 이동거리가 곧 해당 지점에서 2까지 가는 최단거리와 같다.
따라서 2에서 시작해서 BFS를 한다.

문제해결

> 지도의 크기 n,m을 입력받고 지도 배열, 각 지점별 최단거리 배열 result를 nxm크기로 선언한다.
> 방문처리로 중복이동을 막기위해 boolean배열로 visited를 선언하고 사방탐색을 위해 dir, dic배열로 경로를 정의한다.
> result배열에 갈 수 없는 지점의 처리를 위해 -1로 초기화 해준다.
> 지도의 상태를 입력받으며 2는 시작지점으로 start에 저장해두고, 0이라면 못가는 곳이므로 result의 값을 0으로 바꾼다.
> 이제 BFS에 시작점 start의 좌표를 넣호 호출한다.
> 큐를 통해 탐색하고 q의 타입은 배열로 행, 열, 이동횟수를 가진다.
> 시작점의 좌표를 큐의 초기값으로 넣고 이동횟수 0으로 시작하며 방문처리 해주며 while문으로 들어간다.
> 모든 지점을 탐색할것이므로 별도의 종료 로직은 없고 while문이 끝날 때 까지 돈다.
> 큐의 맨 앞값을 cur배열로 잡고 해당 위치의 result값을 갱신해준다.
> 사방탐색으로 새로운 지점, nr,nc를 구하고 유효한 범위인지, 갈 수 있는곳인지, 아직 안갔던 곳인지 체크한다.
> 유효한 지점이면 해당 지점을 큐에 넣고 이동횟수를 +1 해주며 방문처리한다.
> 이렇게 넣은 지점은 while문의 반복에서 cur로 꺼내오며 해당 지점의 result값에 최단거리가 저장된다.

코드

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

public class Main {
    //14940 쉬운 최단거리
    static int n, m;
    static int[][] map, result;
    static boolean[][] visited;
    static int[] dir = {-1, 1, 0, 0}, dic = {0, 0, -1, 1}, start;
    static void BFS(int sr, int sc) {
        Queue<int[]> q = new ArrayDeque<>();
        q.offer(new int[]{sr, sc, 0});
        visited[sr][sc] = true;

        while(!q.isEmpty()) {
            int[] cur = q.poll();
            result[cur[0]][cur[1]] = cur[2];

            for(int i = 0; i < 4; i++) {
                int nr = cur[0] + dir[i];
                int nc = cur[1] + dic[i];

                if(nr < 0 || nr >= n || nc < 0 || nc >= m) continue;
                if(map[nr][nc] == 0) continue;
                if(visited[nr][nc]) continue;
                q.offer(new int[]{nr, nc, cur[2] + 1});
                visited[nr][nc] =  true;
            }
        }
    }
    public static void main(String[] args) throws IOException {
        StringBuilder sb = new StringBuilder();
        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];
        result = new int[n][m];
        visited = new boolean[n][m];

        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                visited[i][j] = false;
                result[i][j] = -1;
            }
        }

        for(int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for(int j = 0; j < m; j++) {
                map[i][j] = Integer.parseInt(st.nextToken());
                if(map[i][j] == 2) start = new int[]{i, j};
                if(map[i][j] == 0) result[i][j] = 0;
            }
        }
        BFS(start[0], start[1]);
        for(int[] i : result) {
            for(int j : i) sb.append(j).append(" ");
            sb.append('\n');
        }
        System.out.println(sb);
    }
}


후기
처음에 모든 지점에서 2까지의 BFS를 다 돌렸다. 1000x1000이라 1,000,000이라서 괜찮을 줄 알았는데 메모리 초과가 났다. 막상 틀리고 생각하니 역으로 2에서 뻗어나가는게 떠올라서 적용하고 해결했다.

0개의 댓글