n
개의 지점이 있고, 각 지점은 출입구, 쉼터 또는 산봉우리입니다.
지점은 양방향 등산로로 연결되어 있고, 이동하는 데 일정 시간이 소요됩니다.
등산코스는 지점 번호를 순서대로 나열하여 표현하며, 등산 중 쉼터나 산봉우리를 방문할 때마다 휴식을 취할 수 있습니다.
등산코스의 intensity
는 휴식 없이 이동하는 가장 긴 시간입니다.
당신은 출입구에서 시작하여 한 곳의 산봉우리만 방문한 후 다시 출입구로 돌아오는 등산코스를 최소 intensity
로 정하려고 합니다.
등산코스에서 출입구는 처음과 끝에 한 번씩, 산봉우리는 한 번만 포함되어야 합니다.
즉, 출입구 → 쉼터 → 산봉우리 → 쉼터 → 출입구(처음 출입구와 같은) 이다.
intensity
가 최소가 되는 등산코스에 포함된 산봉우리 번호와 intensity
의 최솟값을
[산봉우리 번호, intensity 최솟값]
형태로 return 하세요.
단, intensity
가 최소가 되는 등산코스가 여러 개라면 그중 산봉우리의 번호가 가장 낮은 등산코스를 선택한다.
등산코스를 1-2-4-5-6-4-2-1
과 같이 정했을 때의 이동경로를 그림으로 나타내면 아래와 같습니다.
이때, 휴식 없이 이동해야 하는 시간 중 가장 긴 시간은 3시간입니다. 따라서 이 등산코스의 intensity
는 3이며, 이 보다 intensity
가 낮은 등산코스는 없습니다.
가능한 intensity
의 최솟값은 4이며, intensity
가 4가 되는 등산코스는 1-4-1
과 1-7-3-7-1
이 있습니다. intensity
가 최소가 되는 등산코스가 여러 개이므로 둘 중 산봉우리의 번호가 낮은 1-7-3-7-1
을 선택합니다. 따라서 [3, 4]를 return 해야 합니다.
n
≤ 50,000n
- 1 ≤ paths
의 길이 ≤ 200,000paths
의 원소는 [i, j, w]
형태입니다.
i
번 지점과 j
번 지점을 연결하는 등산로가 있다는 뜻입니다.w
는 두 지점 사이를 이동하는 데 걸리는 시간입니다.i
< j
≤ n
w
≤ 10,000,000gates
의 길이 ≤ n
gates
의 원소 ≤ n
gates
의 원소는 해당 지점이 출입구임을 나타냅니다.summits
의 길이 ≤ n
summits
의 원소 ≤ n
summits
의 원소는 해당 지점이 산봉우리임을 나타냅니다.gates
와 summits
에 등장하지 않은 지점은 모두 쉼터입니다.[산봉우리의 번호, intensity의 최솟값]
순서여야 합니다.intensity
를 구하는 것이다.intensity
를 설정했다.출입구 → 쉼터 → 산봉우리 → 쉼터 → 출입구(처음 출입구와 같은)
를 만족한다.출입구 → 쉼터 → 산봉우리
)만 구하면 된다.summits
) 오름차순 정렬을 수행하면 해결할 수 있다.n
의 최대 크기가 50,000이기 때문에, 다익스트라를 처음 수행할 때 모든 출입구를 heap에 넣고 시작해야 한다.import heapq
INF = int(1e9)
def solution(n, paths, gates, summits):
# 다익스트라
def dijkstra():
q = []
dist = [INF] * (n + 1) # 거리 정보 초기화
# 모든 출입구 넣기
for gate in gates:
dist[gate] = 0
heapq.heappush(q, (0, gate))
while q:
intensity, cur = heapq.heappop(q) # intensity가 가장 작은 것을 먼저 꺼낸다.(즉, 출입구부터 꺼내게 됨)
# 최소 intensity를 구해야 하므로
# 산봉우리거나 더 큰 intensity라면 이동하지 않는다.
if cur in summits or dist[cur] < intensity:
continue
# 이동해본다.
for w, nxt in edges[cur]:
cost = max(w, intensity) # 휴식없는 가장 긴 시간이므로
if cost < dist[nxt]:
dist[nxt] = cost
heapq.heappush(q, (cost, nxt))
# 최소 intensity 찾기
ret = [summits[0], dist[summits[0]]]
for summit in summits:
if dist[summit] < ret[1]:
ret[0] = summit
ret[1] = dist[summit]
return ret
# intensity가 최소가 되는 산봉우리가 여러 개일 때
# 산봉우리의 번호가 가장 낮은 등산코스를 선택하기 위해 정렬
summits.sort()
edges = [[] for _ in range(n + 1)]
# 양방향 등산로 정보 초기화
for path in paths:
i, j, w = path
edges[i].append((w, j))
edges[j].append((w, i))
# 다익스트라 수행
answer = dijkstra()
return answer
일단 계속해서 테스트케이스 25번이 시간초과가 났다.
테스트 13 〉 통과 (8.55ms, 11.6MB)
테스트 14 〉 통과 (40.90ms, 20MB)
테스트 15 〉 통과 (303.95ms, 74.3MB)
테스트 16 〉 통과 (375.93ms, 77.1MB)
테스트 17 〉 통과 (385.67ms, 77MB)
테스트 18 〉 통과 (17.86ms, 15.4MB)
테스트 19 〉 통과 (141.27ms, 34MB)
테스트 20 〉 통과 (326.29ms, 71MB)
테스트 21 〉 통과 (412.63ms, 68MB)
테스트 22 〉 통과 (200.47ms, 14.8MB)
테스트 23 〉 통과 (2296.10ms, 30.9MB)
테스트 24 〉 통과 (2288.53ms, 26.6MB)
테스트 25 〉 실패 (시간 초과)
summits
의 최대 크기가 n
이고, n
의 최대 크기는 50,000이다.
q
를 순회할 때 cur in summits
에서 in
은 이 소요된다. (리스트는 , 집합은 )
import heapq
INF = int(1e9)
def solution(n, paths, gates, summits):
# 다익스트라
def dijkstra():
q = []
dist = [INF] * (n + 1) # 거리 정보 초기화
# 모든 출입구 넣기
for gate in gates:
dist[gate] = 0
heapq.heappush(q, (0, gate))
while q:
intensity, cur = heapq.heappop(q)
# 최소 intensity를 구해야 하므로
# 산봉우리거나 더 큰 intensity라면 이동하지 않는다.
if cur in summits_set or dist[cur] < intensity:
continue
# 이동해본다.
for w, nxt in edges[cur]:
new_intensity = max(w, intensity) # 휴식없는 가장 긴 시간이므로
# new_intensity는 확인할 경로의 intensity이고
# dist[nxt]는 이미 저장된 nxt까지의 경로의 intensity이다.
# nxt 위치에 더 작은 intensity로 도착한 적이 있다면
# 큐에 넣지 않는다.
if new_intensity < dist[nxt]:
dist[nxt] = new_intensity
heapq.heappush(q, (new_intensity, nxt))
# 최소 intensity 찾기
ret = [summits[0], dist[summits[0]]]
for summit in summits:
if dist[summit] < ret[1]:
ret[0] = summit
ret[1] = dist[summit]
return ret
# intensity가 최소가 되는 산봉우리가 여러 개일 때
# 산봉우리의 번호가 가장 낮은 등산코스를 선택하기 위해 정렬
summits.sort()
summits_set = set(summits)
edges = [[] for _ in range(n + 1)]
# 양방향 등산로 정보 초기화
for path in paths:
i, j, w = path
edges[i].append((w, j))
edges[j].append((w, i))
answer = dijkstra()
return answer
new_intensity
: 새로운 intensitynxt
: 다음노드w
: nxt가 가지는 intensity(= nxt로 들어가는 intensity
)cur
: 현재노드아이디어에서 언급했듯이, 개선된 다익스트라 알고리즘을 활용한다.
(단, 기준은 가중치의 합이 아닌 intensity이다.)
먼저, heap
에 모든 출입구를 intensity
는 0
으로 설정하고 (0, 출입구)
형태로 넣어준다.
해당 heap을 순회하면서,
continue
) → 산봉우리까지의 편도 경로만 구하면 되므로..intensity
보다 작다면 큐에 넣지 않는다. → 문제에서 최소 intensity
를 구해야 하므로자 이제 연결된 노드로 이동을 해보자.
이때 새로운 intensity
의 설정을 어떻게 하느냐가 중요하다.
intensity[to 현재노드
의 intensity
]와 w[from 현재노드 to 다음노드의 intensity
]를 비교해서 큰 값을 새로운 intensity로 설정한다.
그림으로 보면 다음과 같다.
그리고나서 다음노드까지의 경로의 intensity(dist[nxt]
)와 새로운 intensity를 비교한다.
dist[nxt]
가 의미하는 것은 어떤 특정 노드에서 시작해서 nxt까지의 경로의 intensity
를 의미한다.
즉, 이미 구한 경로의 intensity
이다.
우리가 구하고자 하는 것은 여러 경로 중 최소 intensity를 갖는 경로이므로, 새로운 intensity가 더 작다면 갱신하고 heap에 (새로운 intensity, 다음노드)
를 넣어준다.
(여기서 dist[다음노드]
는 7 → 5
로 갱신됨.)
이를 그림으로 살펴보면 다음과 같다.
단순하게 살펴봤을 때,
- 간선의 개수 : E
- 노드의 개수 : V
→