XX산은 n
개의 지점으로 이루어져 있습니다. 각 지점은 1부터 n
까지 번호가 붙어있으며, 출입구, 쉼터, 혹은 산봉우리입니다. 각 지점은 양방향 통행이 가능한 등산로로 연결되어 있으며, 서로 다른 지점을 이동할 때 이 등산로를 이용해야 합니다. 이때, 등산로별로 이동하는데 일정 시간이 소요됩니다.
등산코스는 방문할 지점 번호들을 순서대로 나열하여 표현할 수 있습니다.
예를 들어 1-2-3-2-1
으로 표현하는 등산코스는 1번지점에서 출발하여 2번, 3번, 2번, 1번 지점을 순서대로 방문한다는 뜻입니다.
등산코스를 따라 이동하는 중 쉼터 혹은 산봉우리를 방문할 때마다 휴식을 취할 수 있으며, 휴식 없이 이동해야 하는 시간 중 가장 긴 시간을 해당 등산코스의 intensity
라고 부르기로 합니다.
당신은 XX산의 출입구 중 한 곳에서 출발하여 산봉우리 중 한 곳만 방문한 뒤 다시 원래의 출입구로 돌아오는 등산코스를 정하려고 합니다. 다시 말해, 등산코스에서 출입구는 처음과 끝에 한 번씩, 산봉우리는 한 번만 포함되어야 합니다.
당신은 이러한 규칙을 지키면서 intensity
가 최소가 되도록 등산코스를 정하려고 합니다.
다음은 XX산의 지점과 등산로를 그림으로 표현한 예시입니다.
위의 예시에서 1-2-5-4-3
과 같은 등산코스는 처음 출발한 원래의 출입구로 돌아오지 않기 때문에 잘못된 등산코스입니다. 또한 1-2-5-6-4-3-2-1
과 같은 등산코스는 코스의 처음과 끝 외에 3번 출입구를 방문하기 때문에 잘못된 등산코스입니다.
등산코스를 3-2-5-4-3
과 같이 정했을 때의 이동경로를 그림으로 나타내면 아래와 같습니다.
이때, 휴식 없이 이동해야 하는 시간 중 가장 긴 시간은 5시간입니다. 따라서 이 등산코스의 intensity
는 5입니다.
등산코스를 1-2-4-5-6-4-2-1
과 같이 정했을 때의 이동경로를 그림으로 나타내면 아래와 같습니다.
이때, 휴식 없이 이동해야 하는 시간 중 가장 긴 시간은 3시간입니다. 따라서 이 등산코스의 intensity
는 3이며, 이 보다 intensity
가 낮은 등산코스는 없습니다.
XX산의 지점 수 n
, 각 등산로의 정보를 담은 2차원 정수 배열 paths
, 출입구들의 번호가 담긴 정수 배열 gates
, 산봉우리들의 번호가 담긴 정수 배열 summits
가 매개변수로 주어집니다. 이때, intensity
가 최소가 되는 등산코스에 포함된 산봉우리 번호와 intensity
의 최솟값을 차례대로 정수 배열에 담아 return 하도록 solution 함수를 완성해주세요. intensity
가 최소가 되는 등산코스가 여러 개라면 그중 산봉우리의 번호가 가장 낮은 등산코스를 선택합니다.
n | paths | gates | summits | result |
---|---|---|---|---|
6 | [[1, 2, 3], [2, 3, 5], [2, 4, 2], [2, 5, 4], [3, 4, 4], [4, 5, 3], [4, 6, 1], [5, 6, 1]] | [1, 3] | [5] | [5, 3] |
7 | [[1, 4, 4], [1, 6, 1], [1, 7, 3], [2, 5, 2], [3, 7, 4], [5, 6, 6]] | [1] | [2, 3, 4] | [3, 4] |
7 | [[1, 2, 5], [1, 4, 1], [2, 3, 1], [2, 6, 7], [4, 5, 1], [5, 6, 1], [6, 7, 1]] | [3, 7] | [1, 5] | [5, 1] |
5 | [[1, 3, 10], [1, 4, 20], [2, 3, 4], [2, 4, 6], [3, 5, 20], [4, 5, 6]] | [1, 2] | [5] | [5, 6] |
이 문제는 다익스트라 문제의 응용 버전이라고 할 수 있다.
일단 왕복이라는 것에 속지말자.
왕복이라는 말은 사실상 최단거리를 그대로 따라오면 되는 것이기 때문에, 다익스트라로 최단 거리 한 번만 구해두면 된다.
그런데 문제에서는 정상을 찍고 돌아오는 최단 거리를 요구하는 것이 아니라, 최단 거리로 정상을 찍고 돌아오는 과정에서 최대로 오래 걸리는 등산 코스가 몇인지를 요구하고 있다.
이 문제를 잘 이해 해야지 다익스트라의 응용임을 떠올릴 수 있다.
해당 강의 영상을 참고했다.
우선 입력받은 paths
를 통해서 그래프를 생성한다.
무방향 그래프이다.
그리고, 우리는 다익스트라 알고리즘을 이용해서 최대로 오래 걸리는 등산 코스를 찾아야 하기 때문에 intensities
를 float(inf)
로 초기화 한다.
출발 지점은 문제에서 제공하기 때문에 gates
를 확인하면서, 해당 출발 지점의 intensity
는 0으로 만든다.
다익스트라 알고리즘을 위한 힙에 각 출발점을 추가 해준다.
힙을 돌면서,
만약 현재의 intensity
가 지금까지 탐색했던 intensity
보다 높은 경우 패스한다.
패스하는 이유는 가장 높은 intensity
를 찾는 것은 맞지만, 그건 최단 거리로 산을 등산하는 경우에 한 해서이기 때문이다.
그리고 정상의 경우에도 역시 패스한다.
그러면 이제 다음 등산코스를 선택할텐데, 여기서 다익스트라 알고리즘과 다른 방식으로 동작한다.
여기서 현재 거리와 다음 노드까지의 최단 거리를 갱신하는 것이 아니라, 현재 intensity
와 다음 intensity
를 가지고 intensities
를 갱신한다.
현재 intensity
와 다음 intensity
중 큰 것을 선택하고, 그것이 만약 지금까지 탐색한 노드의 intensity
보다 작다면 갱신하고 힙에 추가하는 것이다.
→ 이 부분이 이 문제의 핵심이다. 더 큰 값을 선택하고 더 작은 경우 갱신하는 이유는 다익스트라 알고리즘의 원리를 따라야 하기 때문이다.
위 과정은 최단 거리로 이동하는 경로 속에서 그 동안 경로안에 최대 intensity
만큼 이동할 수 있는 것이다.
만약 여기서 그 동안 경로 (현재 포함) 보다 더 큰 intensity
경로를 선택하게 되는 경우, 최단 거리로 이동하는 경로가 아니게 된다.
그렇게 각 최단 높이를 구했으면, 이제 정상을 선택해야 한다.
정상은 여러개가 주어질 수 있으므로, 최대 높이를 가진 정상을 선택해야 정답이 된다.
항상 드는 생각이지만, 정말 한 끝 차이로 사고를 못해서 틀리는 것 같다.
왜 항상 풀때는 아이디어를 못 떠올릴까..
from collections import defaultdict
from heapq import heappop, heappush
def solution(n, paths, gates, summits):
graph = defaultdict(set)
for i, j, w in paths:
graph[i].add((j, w))
graph[j].add((i, w))
intensities = [float('inf')] * (n+1)
heap = []
for gate in gates:
intensities[gate] = 0
heappush(heap, (0, gate))
while heap:
intensity, node = heappop(heap)
if intensities[node] < intensity or node in summits:
continue
for neighbor, weight in graph[node]:
next_intensity = max(intensity, weight)
if next_intensity < intensities[neighbor]:
intensities[neighbor] = next_intensity
heappush(heap, (next_intensity, neighbor))
answer = [-1, float('inf')]
summits = set(summits)
for summit in sorted(summits):
if intensities[summit] < answer[1]:
answer = [summit, intensities[summit]]
return answer