D - 安全な登山ルート / Safe Climbing Route 解説 by admin 印刷ページ
claude4.8opus-highOverview
This problem asks us to find a route from point \(1\) to point \(N\) with a total travel time of at most \(T\), such that the maximum danger level of the points visited along the route is minimized. This can be solved using binary search + Dijkstra’s algorithm.
Analysis
First, we want to find the “minimum of the maximum danger level of the points along the path”. Problems of the form “minimize the maximum value” are well-known to be highly compatible with binary search (parametric search).
Focusing on Monotonicity
Let us fix a danger level threshold \(X\) and consider the following decision problem:
Is it possible to reach point \(N\) from point \(1\) in at most time \(T\) using only points with danger level at most \(X\)?
The key observation here is that this decision problem has monotonicity.
- If it is possible to reach the destination with threshold \(X\), it is obviously possible with any larger threshold \(X'\) (since we only increase the set of allowed points).
- Conversely, if it is impossible with threshold \(X\), it is obviously impossible with any smaller threshold.
In other words, there is a boundary value where the status switches from “impossible” to “possible”. This boundary value is the answer we are looking for.
Solving the Decision Problem
When the threshold \(X\) is fixed, we can solve the decision problem by finding the shortest time to travel from point \(1\) to point \(N\) under the constraint that “only points with danger level at most \(X\) can be used”, and checking if this shortest time is at most \(T\). The shortest path can be found using Dijkstra’s algorithm.
As a caveat, if the danger level of point \(1\) or point \(N\) itself exceeds \(X\), it is impossible to even start or finish. We can handle this naturally by restricting the search range of the answer to be at least \(\max(P_1, P_N)\).
Drawbacks of a Naive Approach
A naive linear search that tries all possible danger levels in ascending order to find the first one that allows us to reach the destination would yield the correct answer. However, there are up to \(N\) candidate danger levels. Running Dijkstra’s algorithm (\(O(M \log N)\)) for each would result in a total time complexity of \(O(N \cdot M \log N)\), which is too slow when \(N\) and \(M\) are large.
Therefore, by utilizing monotonicity and performing a binary search, we can reduce the number of decision problem calls to \(O(\log N)\) times.
Algorithm
- Prepare the candidate danger levels. Since the answer must be one of the danger levels \(P_i\) of the points, we collect all \(P_i \ge \max(P_1, P_N)\), remove duplicates, and sort them.
- Perform a binary search on the sorted candidate array. For the middle candidate value \(X\), solve the decision problem (whether point \(N\) can be reached in at most \(T\) time using only points with danger level at most \(X\)) using Dijkstra’s algorithm.
- If reachable, try smaller candidate values (and update the answer).
- If unreachable, try larger candidate values.
- The minimum candidate value that ultimately allows reaching the destination is the answer.
Details of the Decision Problem (Dijkstra’s Algorithm)
- For each point \(i\), allow visits only when “danger level \(P_i \le X\)”.
- Find the shortest time from the start (point \(1\)) to all other points, and check if the shortest time to point \(N\) is at most \(T\).
- In implementation, you only need to check \(P_v \le X\) when transitioning to an adjacent vertex \(v\). (The start point \(1\) is guaranteed to satisfy this due to the lower bound of candidate values).
Complexity
Let \(K\) be the number of unique candidate values (at most \(N\)):
- Time Complexity: \(O(M \log N \cdot \log K) = O(M \log^2 N)\)
- A single run of Dijkstra’s algorithm takes \(O(M \log N)\), and it is called \(O(\log K)\) times during the binary search.
- Space Complexity: \(O(N + M)\)
- To store the adjacency list and the distance array.
This is sufficiently fast given the constraints \(N \le 2 \times 10^4\) and \(M \le 4 \times 10^4\).
Implementation Points
Lower bound of candidate values: Since the start and goal points must be visited, the answer is always at least \(\max(P_1, P_N)\). Restricting the candidate values to be at least this value eliminates cases where the start or goal points are unusable, making the checks within Dijkstra’s algorithm simpler.
Early termination of Dijkstra’s algorithm: The distance to the goal point \(N\) is finalized as soon as it is popped from the priority queue (heap). Terminating the search and returning the result at this point improves efficiency.
Handling multiple edges: Multiple trails may exist between the same two points, but Dijkstra’s algorithm will work correctly if they are all added directly to the adjacency list.
Guarantee of answer existence: Under the problem constraints, there is always at least one path that can be traversed in at most \(T\) time. Therefore, the destination is guaranteed to be reachable at the upper bound of the binary search (the maximum candidate value), and we will always find a valid answer.
Source Code
import sys, heapq
def main():
data = sys.stdin.buffer.read().split()
idx = 0
N = int(data[idx]); idx+=1
M = int(data[idx]); idx+=1
T = int(data[idx]); idx+=1
P = [0]*N
for i in range(N):
P[i] = int(data[idx]); idx+=1
# adjacency
head = [[] for _ in range(N)]
for _ in range(M):
u = int(data[idx])-1; idx+=1
v = int(data[idx])-1; idx+=1
w = int(data[idx]); idx+=1
head[u].append((v,w))
head[v].append((u,w))
INF = float('inf')
target = N-1
def feasible(X):
# vertices with P[i] <= X allowed
dist = [INF]*N
dist[0] = 0
heap = [(0,0)]
while heap:
d,u = heapq.heappop(heap)
if d > dist[u]:
continue
if u == target:
return d <= T
for v,w in head[u]:
if P[v] <= X:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heapq.heappush(heap,(nd,v))
return dist[target] <= T
lo = P[0] if P[0] > P[target] else P[target]
# candidate values: distinct P values >= lo
cands = sorted(set(p for p in P if p >= lo))
# binary search smallest cands[i] feasible
left = 0
right = len(cands)-1
ans = cands[right]
while left <= right:
mid = (left+right)//2
if feasible(cands[mid]):
ans = cands[mid]
right = mid-1
else:
left = mid+1
sys.stdout.write(str(ans)+"\n")
main()
This editorial was generated by claude4.8opus-high.
投稿日時:
最終更新: