등산코스 정하기

2025-11-05
#JAVA#프로그래머스
2

문제 풀이 시간 : 1시간15분

문제 요약

  • 산의 지점들 중 출입구 → 산봉우리 → 출입구로만 이동해야 함.

  • 경로에서 지나는 등산로 시간 중 가장 큰 값 = intensity.

  • 모든 산봉우리 중, intensity가 가장 작은 산봉우리를 찾는다.(동률이면 번호가 작은 것)

문제 풀이

이 문제는 얼핏 보면

“출입구에서 각 산봉우리까지 왕복하며 최댓값이 최소가 되는 경로”

Minimax Path 문제로 보인다.

즉, 단순한 최단 거리(다익스트라)처럼 간선들의 합을 최소화하는 것이 아니라

경로 중 가장 큰 간선(시간)을 최소화해야 한다.

그래서 처음엔 산봉우리에서 BFS로 출입구까지 탐색하면서

최댓값을 갱신해가는 방식을 시도했다.

하지만, 이 방식은 문제가 많았고 실제로 오답이 나왔다.

(→ 뒤에서 “실패 코드”로 정리)


핵심 아이디어

  • 목표: intensity = 경로 중 최대 간선 가중치를 최소화

  • 이때, 출입구는 여러 곳일 수 있고

    산봉우리도 여러 곳이므로

    모든 출입구에서 시작해 동시에 탐색하는 방식이 유리하다.

  • 즉,

    multi-source Dijkstra(다중 시작점 다익스트라)

    +

    비용 정의 = 누적합이 아니라 max(edge weight)를 활용한다.

  • 또한, 산봉우리에 도착하면 더 이상 확장할 필요가 없다.

    (산봉우리는 딱 한 번만 방문해야 하고, 이후 경로는 고려 불필요)


실패 접근

처음 시도할 때는,

각 산봉우리(summit)에서 출발 → 출입구까지 도달

할 때 발생하는 intensity를 계산하는 방식이었다.

Java
for (각 summit) { BFS → gate에 도달할 때까지 탐색 intensity = 경로 내 최대 간선 최소값 갱신 }

겉으로 보면 될 것 같았으나, 실제로는

  • 모든 산봉우리마다 BFS/Dijkstra → 비효율

  • 탐색 방향 제약이 사실상 없어

    “중간에 다른 출입구를 지나가는 잘못된 코스”도 걸러내기 어려움

그래서 조건을 제대로 만족시키기 어렵고

시간/메모리도 비효율적이라 결국 실패했다.

실패 코드

Java
for(int i=0;i<summits.length;i++){ int[] dist = new int[n+1]; boolean[] visited = new boolean[n+1]; int s = summits[i]; Arrays.fill(dist,Integer.MAX_VALUE); dist[s] = 0; Queue<Node> q = new ArrayDeque<>(); q.add(new Node(s,0)); while(!q.isEmpty()){ Node now = q.poll(); if(now.dist>dist[now.num]) continue; dist[now.num] = now.dist; if(gate.contains(now.num)){ if(answer[1]<now.dist) continue; answer[0] = s; answer[1] = now.dist; continue; } for(Node next : arr[now.num]){ int nd = Math.max(now.dist, next.dist); if(dist[next.num]<nd) continue; q.add(new Node(next.num, nd)); } } }

다중 시작점 다익스트라 + Max Edge Cost 누적 방식

  • 출입구들을 모두 시작점으로 하여intensity[node] = 그 지점까지 도달할 때의 최소 intensity로 정의하고 갱신해간다.

  • 일반적인 다익스트라는 cost = prev_cost + edge이지만,

    여기서는cost = max(prev_cost, edge)이다.

  • 또한, 도중에 산봉우리에 도착하면 그 노드는 확장하지 않는다.

  • 최종적으로 모든 산봉우리 중 intensity[s]가 가장 작은 산봉우리를 선택 (동률 시 번호가 작은 것)

왜 출입구에서 출발해야 하나?

  • 문제 규칙상

    “출입구는 처음과 끝에만 등장해야 한다”

  • 즉, 코스의 진입과 탈출은 항상 출입구.

  • 그러므로, 출입구에서 시작해 각 지점까지 intensity를 갱신해가는 게 자연스러움.


정답 코드

Java
import java.util.*; class Solution { public class Node{ public int num; public int dist; public Node(int num, int dist){ this.num = num; this.dist = dist; } } public int[] solution(int n, int[][] paths, int[] gates, int[] summits) { int[] answer = {Integer.MAX_VALUE, Integer.MAX_VALUE}; List<Node>[] arr = new ArrayList[n+1]; for(int i=1;i<=n;i++){ arr[i] = new ArrayList<>(); } Set<Integer> summit = new HashSet<>(); for(int s : summits){ summit.add(s); } for(int[] p : paths){ int f = p[0], t = p[1], d = p[2]; arr[f].add(new Node(t, d)); arr[t].add(new Node(f, d)); } PriorityQueue<Node> pq = new PriorityQueue<>((a,b)->a.dist-b.dist); int[] intensity = new int[n+1]; Arrays.fill(intensity, Integer.MAX_VALUE); for(int g : gates){ intensity[g] = 0; pq.add(new Node(g, 0)); } while(!pq.isEmpty()){ Node now = pq.poll(); if(intensity[now.num] < now.dist) continue; if(summit.contains(now.num)) continue; for(Node next : arr[now.num]){ int cost = Math.max(now.dist, next.dist); if(cost < intensity[next.num]){ intensity[next.num] = cost; pq.add(new Node(next.num, cost)); } } } Arrays.sort(summits); for(int s : summits){ if(intensity[s] < answer[1]){ answer[0] = s; answer[1] = intensity[s]; } } return answer; } }