D - 配達ルートの最適化 / Optimizing Delivery Routes Editorial by admin
Gemini 3.1 Pro (Thinking)Overview
This problem asks you to find the shortest path (minimum travel time) from vertex \(1\) to vertex \(N\) in an undirected graph where some edge weights have been modified.
Analysis
This problem can be reduced to a shortest path problem on a weighted undirected graph, where each location is a “vertex,” each road is an “edge,” and the travel time is the “edge weight.”
Since all edge weights are non-negative, Dijkstra’s Algorithm is the optimal choice. If we were to use a naive depth-first search (DFS) that explores all possible path combinations, the computational complexity would grow exponentially, resulting in TLE (Time Limit Exceeded). Even using the Bellman-Ford algorithm would take \(O(NM)\) time, which would also result in TLE under the given constraints (\(N, M \le 2 \times 10^5\)).
The increase in travel time due to speed restrictions can be handled during the graph construction phase. We can store the road numbers subject to speed restrictions in a set (Set) beforehand, and when registering edge information in the adjacency list, simply double the weight if that road is subject to restrictions.
Algorithm
- Input and Preprocessing:
- Store the road numbers \(C_1, C_2, \ldots, C_K\) subject to speed restrictions in a hash set (Set) for fast lookup.
- Graph Construction:
- Create an “adjacency list” to manage the edges connected to each vertex.
- When adding road \(i\) to the adjacency list, if road number \(i\) is in the set, set the travel time to \(2W_i\); otherwise, set it to \(W_i\). Add edges in both directions.
- Running Dijkstra’s Algorithm:
- Prepare an array
distto manage the shortest distance from vertex \(1\) to each vertex, initialized to infinity (\(\infty\)), withdist[1]set to \(0\). - Insert
(distance 0, vertex 1)into the priority queue (heap). - Extract the vertex \(u\) with the currently known shortest distance from the queue, and for each neighboring vertex \(v\), if
distance to vertex u + edge weightis less than the currentdist[v], updatedist[v]and add(new distance, vertex v)to the queue. - Repeat this until the queue is empty or the shortest distance to the target vertex \(N\) is determined.
- Prepare an array
- Output the Answer:
- If
dist[N]remains infinity, the destination is unreachable, so output-1; otherwise, outputdist[N].
- If
Complexity
- Time Complexity: \(O(M \log M)\) or \(O(M \log N)\)
- Graph construction takes \(O(M)\), and building the speed restriction set takes \(O(K)\).
- In Dijkstra’s algorithm, insertions and extractions from the priority queue occur at most \(M\) times, resulting in \(O(M \log M)\) time.
- Overall, the complexity is \(O(M \log M)\), which runs sufficiently fast under the given constraints.
- Space Complexity: \(O(N + M)\)
- Memory is needed for the adjacency list proportional to the number of vertices \(N\) and edges \(M\), as well as the distance array and priority queue.
Implementation Tips
Setting Infinity (INF): From the constraints, the maximum travel time per edge is \(10^9\), and a path may traverse up to \(N-1\) edges. Therefore, the maximum shortest path length can be approximately \(2 \times 10^5 \times 10^9 = 2 \times 10^{14}\). Thus, the infinity value used to initialize distances must be set to a sufficiently large value such as \(10^{18}\).
Fast I/O: Since the amount of input data is large, in Python, reading all input at once using
sys.stdin.read().split()can significantly reduce execution time.Skipping Unnecessary Exploration: Within the Dijkstra loop, if the distance
dextracted from the queue is greater than the already recordeddist[u], it is outdated (non-optimal) information, so it is important to skip it withcontinue. This prevents unnecessary computation.Source Code
import sys
import heapq
def solve():
input_data = sys.stdin.read().split()
if not input_data:
return
N = int(input_data[0])
M = int(input_data[1])
K = int(input_data[2])
idx = 3
U = [0] * (M + 1)
V = [0] * (M + 1)
W = [0] * (M + 1)
for i in range(1, M + 1):
U[i] = int(input_data[idx])
V[i] = int(input_data[idx+1])
W[i] = int(input_data[idx+2])
idx += 3
restricted = set()
if K > 0:
for _ in range(K):
restricted.add(int(input_data[idx]))
idx += 1
adj = [[] for _ in range(N + 1)]
for i in range(1, M + 1):
u = U[i]
v = V[i]
w = W[i]
if i in restricted:
w *= 2
adj[u].append((v, w))
adj[v].append((u, w))
INF = 10**18
dist = [INF] * (N + 1)
dist[1] = 0
pq = [(0, 1)]
while pq:
d, u = heapq.heappop(pq)
if d > dist[u]:
continue
if u == N:
break
for v, w in adj[u]:
if dist[v] > d + w:
dist[v] = d + w
heapq.heappush(pq, (dist[v], v))
if dist[N] == INF:
print(-1)
else:
print(dist[N])
if __name__ == '__main__':
solve()
This editorial was generated by gemini-3.1-pro-thinking.
posted:
last update: