백준 1486 등산 java

Dec 17, 2025
백준 1486 등산 java

1. 문제에 대하여 📦

  1. 2차원 배열 MAP이 주어진다. (i,j) 좌표에는 해당 좌표의 고도가 주어진다.
  1. (0,0)은 호텔이 있는 지점이다. 호텔에서부터 다른 좌표의 고도를 탐방할 예정인데, 다음 규칙을 지킨다.
    1. 현 고도 >= 넘어갈 고도 이면 시간이 1초 소모
    2. 현 고도 < 넘어갈 고도 이면 $( \text{넘어갈 고도} - \text(현 고도) )^{2}$ 초가 든다.
    3. D라는 시간제한이 주어지고 이 시간안에 호텔로 다시 돌아와야 한다.
  1. 세준이가 호텔을 떠나서 다시 되돌아올 때까지 건널 수 있는 가장 높은 고도는 얼마인가?

(1) 조건 분석 📊

모든 좌표를 시작점에서 가장 최단 경로로 일단 가야지 최대한 많은 경로를 발견할 수 있고, 많은 경로를 발견해야 최대한 높은 고도도 답 범주에 넣을 수 있다는 뜻이다. 따라서 시작점 -> 다른 좌표까지의 다익스트라가 필요하다.
더불어 돌아올 떄도 임의의 좌표 (i,j)로부터 (0,0)까지 최단경로로 와야지, D 시간에 오는 경로를 최대한 많이 계산할 수 있기에, (i,j) -> (0,0) 또한 다익스트라가 필요하다.

2. 코드가 나오기까지 🛠️

KEY WORD: 두번의 다익스트라
문제 설명에서 설명한대로 두 번의 다익스트라를 하면 답이 나온다. 여기서 두 가지 풀이법이 존재한다.

(1) 4차원 배열 이용 모든 (시작,도착) 경우의 수의 다익스트라 다 구하기

행,렬 크기가 25이하이므로, int 배열을 4차원으로 해도 최대 1.5GB밖에 차지 하지 않는다. 또한 모든 R,C 좌표를 시작점으로 해서 다익스트라를 구한다고 해도, $25 \times 25 \times 625(\log{625})$ 정도의 연산 횟수가 필요하므로 시간초과도 나지 않는다.
4차원 배열dp을 만들고, dp(시작행, 시작열, 도착행, 도착열) 를 다익스트라로 채우면 된다. 밑에 풀이 설명

(2) 다익스트라 딱 두 번으로 구하기

  1. (0,0) 출발 - 모든 점까지 도착을 다익스트라로 구하기
  1. 모든 점 출발 - (0,0) 도착을 다익스트라로 구하기
두개의 dashboard 배열만 채우면 된다.
보통의 다익스트라의 경우, 가중치가 간선에 붙어 있기 때문에, 임의의 값 (0,0)과 (i,j) 중 어느 지점이 출발점이냐 도착점이냐의 상관없이 둘 사이의 최단 경로는 같다.
image.png
하지만 이번 문제의 경우, 출발점이나 도착점이 어디냐에 따라서 둘 사이 가중치 값이 달라진다.
image.png
따라서 (0,0)에서 출발, 모든 점까지의 최단 경로와 모든 점에서 출발하여 0,0까지의 최단 경로 다익스트라를 한 번씩 구하면 된다.
notion image
모든 점에서 출발하여 0,0까지의 최단 경로는 고도 계산에서 시작 고도와 끝 고도 위치를 매번 다르게 하면 풀린다. 그렇게하면, 오른쪽 배열의 i,j 좌표에는 0,0까지의 최단 경로가 저장된다.

(1) 시간복잡도 분석 ⏳

배열의 크기 최대 625, 간선은 하나의 좌표당 4개씩 붙었디고 치자. 그러혐ㄴ 간선의 개수는 2500개
다익스트라 두 번하는 것이므로 시간 복잡도는
O(2500×log625)O(2500 \times \log{625}) 이다.

3. 코드 🌐

A. 1번 풀이

import java.util.*; import java.io.*; // 1486 등산 public class Main { static class Coor{ int r; int c; int t; public Coor(int r, int c, int t){ this.r = r; this.c = c; this.t = t; } } static int R,C,H,D; static int [][] map; static int [][][][] dp; static int [][] dir = new int [][] {{0,1}, {0,-1}, {1,0}, {-1,0}}; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); R = Integer.parseInt(st.nextToken()); C = Integer.parseInt(st.nextToken()); H = Integer.parseInt(st.nextToken()); D = Integer.parseInt(st.nextToken()); map = new int [R][C]; dp = new int [R][C][R][C]; for(int i = 0; i < R; i++){ String row = br.readLine(); for(int j = 0; j < C; j++){ int h = row.charAt(j); if(65 <= h && h <= 90) h -= 65; if(97 <= h && h <= 122) h -= 71; map[i][j] = h; } } for(int i = 0; i < R; i++){ for(int j = 0; j < C; j++){ for(int k = 0; k < R; k++){ Arrays.fill(dp[i][j][k], 1_000_001); } } } for(int i = 0; i < R; i++){ for (int j = 0; j < C; j++){ dijkstra(i,j); } } int max = 0; for(int i = 0; i < R; i++){ for (int j = 0; j < C; j++){ if(dp[0][0][i][j] + dp[i][j][0][0] <= D) max = Math.max(max, map[i][j]); } } System.out.printf("%d", max); } public static void dijkstra(int r, int c){ PriorityQueue<Coor> pq = new PriorityQueue<>(Comparator.comparingInt(o -> o.t)); pq.add(new Coor(r,c,0)); dp[r][c][r][c] = 0; while (!pq.isEmpty()){ Coor now = pq.poll(); if(now.t > dp[r][c][now.r][now.c]) continue; for(int i = 0; i < 4; i++){ int nr = now.r + dir[i][0]; int nc = now.c + dir[i][1]; if(OOB(nr,nc)) continue; if(Math.abs(map[now.r][now.c] - map[nr][nc]) > H) continue; int spend_time = map[now.r][now.c] >= map[nr][nc]? 1 : (int) Math.pow(map[now.r][now.c]-map[nr][nc],2); if(now.t + spend_time >= dp[r][c][nr][nc]) continue; if(now.t + spend_time > D) continue; dp[r][c][nr][nc] = now.t + spend_time; pq.add(new Coor(nr,nc,dp[r][c][nr][nc])); } } } public static boolean OOB (int r, int c){ return r < 0 || c < 0 || r >= R || c >= C; } }

B. 2번 풀이

import java.util.*; import java.io.*; import java.util.function.Function; // 1486 등산 public class Main { static class Coor{ int r; int c; int t; public Coor(int r, int c, int t){ this.r = r; this.c = c; this.t = t; } } static int R,C,H,D; static int [][] map; static int [][] dir = new int [][] {{0,1}, {0,-1}, {1,0}, {-1,0}}; static int [][][] dp; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); R = Integer.parseInt(st.nextToken()); C = Integer.parseInt(st.nextToken()); H = Integer.parseInt(st.nextToken()); D = Integer.parseInt(st.nextToken()); map = new int [R][C]; dp = new int[R][C][2]; for(int i = 0; i < R; i++){ String row = br.readLine(); for(int j = 0; j < C; j++){ int h = row.charAt(j); if(65 <= h && h <= 90) h -= 65; if(97 <= h && h <= 122) h -= 71; map[i][j] = h; } } for(int i = 0; i < R; i++){ for(int j = 0; j < C; j++){ dp[i][j][0] = 1_000_001; dp[i][j][1] = 1_000_001; } } dp[0][0][0] = 0; dp[0][0][1] = 1; dijkstra(0, (prevR, prevC, nowR, nowC) -> (map[prevR][prevC] >= map[nowR][nowC]? 1 : (int)Math.pow(map[prevR][prevC]-map[nowR][nowC], 2))); dijkstra(1, (prevR, prevC, nowR, nowC) -> (map[prevR][prevC] <= map[nowR][nowC]? 1 : (int)Math.pow(map[prevR][prevC]-map[nowR][nowC], 2))); int max = 0; for(int i = 0; i < R; i++){ for(int j = 0; j < C; j++){ if(dp[i][j][0] + dp[i][j][1] <= D) max = Math.max(max, map[i][j]); } } System.out.println(max); } public static void dijkstra (int dimension, CalculateDistance calculateDistance ) { PriorityQueue<Coor> pq = new PriorityQueue<>(Comparator.comparingInt(o-> o.t)); pq.add(new Coor(0,0, 0)); while (!pq.isEmpty()){ Coor now = pq.poll(); if(now.t > dp[now.r][now.c][dimension]) continue; for(int i = 0; i < 4; i++){ int nr = now.r + dir[i][0]; int nc = now.c + dir[i][1]; if(OOB(nr, nc)) continue; if(Math.abs(map[now.r][now.c] - map[nr][nc]) > H) continue; int spending_time = calculateDistance.cal(now.r, now.c, nr, nc); if(now.t + spending_time >= dp[nr][nc][dimension]) continue; dp[nr][nc][dimension] = now.t+spending_time; pq.add(new Coor(nr,nc, dp[nr][nc][dimension])); } } } public static boolean OOB(int r, int c){ return r < 0 || c < 0 || r >= R || c >= C; } @FunctionalInterface public interface CalculateDistance{ Integer cal(int prevR, int prevC, int nowR, int nowC); } }

4. 트러블 슈팅 or 배운 점📝

다익스트라를 항상 하나의 정점에서 다른 모든 정점까지의 최단 경로라고만 생각했다. 즉 출발 정점에서 뻗어나가는 단 방향 흐름으로만 이해했었는데, 해당 문제를 풀고 오히려 다른 모든 정점에서 출발정점까지 가는 최단 경로라고 반대로 생각할 수도 있음을 배웠다.
image.png