D - 街歩きと通行料 / City Walk and Tolls Editorial by admin
Gemini 3.0 Flash (Thinking)Overview
This problem requires finding the shortest path (minimum cost) from point \(1\) to point \(N\), considering both road travel times (edge weights) and intersection tolls (vertex weights).
Analysis
Dijkstra’s algorithm, a well-known shortest path algorithm, typically handles only edge weights (road travel times). However, in this problem, costs are also incurred when passing through vertices (intersections).
The key insight is to consider the cost of the action “moving from intersection \(u\) to adjacent intersection \(v\)” as (road travel time \(W\)) + (toll at destination intersection \(v\), \(C_v\)).
However, with this approach alone, the “toll at the starting point (intersection \(1\))” would be missed in the calculation. Since the problem statement specifies that “tolls are also added if the starting point or destination has a toll area,” we can correctly compute the answer by setting the initial state — “the cost upon reaching intersection \(1\)” — to “the toll at intersection \(1\).”
Since all edge weights (road travel times) and vertex weights (tolls) are non-negative, Dijkstra’s algorithm can be applied to solve this problem efficiently.
Algorithm
We use Dijkstra’s algorithm for solving the single-source shortest path problem on a weighted graph.
- Initialization:
- Initialize an array
distthat holds the minimum cost to each intersection with infinity (e.g., \(10^{18}\)). - Set
dist[1]to “the toll at intersection \(1\).” - Prepare a priority queue (heap) and add
(dist[1], 1)to it.
- Initialize an array
- Exploration:
- Extract the intersection \(u\) with the minimum cost \(d\) from the queue.
- If
d > dist[u], this information is outdated, so skip it. - For each intersection \(v\) adjacent to intersection \(u\) (with road weight \(w\)), perform the following calculation:
new cost = d + w + (toll at intersection v)- If
new cost < dist[v], updatedist[v]and add(new cost, v)to the queue.
- Termination:
- The value of
dist[N]when intersection \(N\) is reached, or when the queue becomes empty, is the answer.
- The value of
Complexity
- Time complexity: \(O((N + M) \log N)\)
- This is the complexity of Dijkstra’s algorithm using a priority queue. Each edge is examined at most once, and each insertion/extraction operation on the queue costs \(O(\log N)\).
- Space complexity: \(O(N + M)\)
- This is needed for the graph in adjacency list format, the cost array for each vertex, and the toll array.
Implementation Notes
Storing tolls: Since not all intersections have toll areas, it is efficient to create an array
tollswith a default value of \(0\) and overwrite it with the information for the given \(K\) locations.Handling large values: Since \(N, M\) can be up to \(2 \times 10^5\) and each cost can be up to \(10^9\), the total cost can exceed \(10^{14}\). The value used as infinity in the program must be set sufficiently large.
Fast I/O: Since \(N, M\) can be large, in Python it is beneficial to read all input at once using
sys.stdin.read().split()to improve execution speed.Source Code
import heapq
import sys
def solve():
# 入力を一度にすべて読み込み、スペースや改行で分割してトークンのリストを作成します
try:
input_data = sys.stdin.read().split()
except EOFError:
return
if not input_data:
return
# イテレータを使用してトークンを1つずつ処理します(メモリ効率と速度のため)
tokens = map(int, input_data)
# 交差点の数 N、道路の数 M、有料エリアの数 K を取得
try:
N = next(tokens)
M = next(tokens)
K = next(tokens)
except StopIteration:
return
# 隣接リストを作成してグラフを表現します
# 各交差点(1-indexed)に対して、(接続先, 所要時間) のリストを保持します
adj = [[] for _ in range(N + 1)]
for _ in range(M):
try:
u = next(tokens)
v = next(tokens)
w = next(tokens)
# 道路は双方向に通行可能です
adj[u].append((v, w))
adj[v].append((u, w))
except StopIteration:
break
# 各交差点の通行料を格納する配列を作成します
tolls = [0] * (N + 1)
for _ in range(K):
try:
l = next(tokens)
c = next(tokens)
tolls[l] = c
except StopIteration:
break
# ダイクストラ法を用いて最小コストを求めます
# dist[i] は交差点 1 から交差点 i までの最小コストを保持します
inf = 10**18
dist = [inf] * (N + 1)
# 出発地点(交差点 1)の通行料を初期コストとして設定します
dist[1] = tolls[1]
# 優先度付きキュー(ヒープ)には (現在の総コスト, 現在の交差点) を格納します
pq = [(dist[1], 1)]
while pq:
d, u = heapq.heappop(pq)
# すでに確定している最小コストより大きい場合はスキップします
if d > dist[u]:
continue
# 目的地である交差点 N に到達した場合、最初に取り出されたときが最小コストです
if u == N:
break
# 隣接するすべての交差点を探索します
for v, w in adj[u]:
# 新しい地点 v への移動コスト = 現在までのコスト + 道路の所要時間 + 到着した交差点の通行料
new_cost = d + w + tolls[v]
# これまでに見つかったコストより小さい場合、情報を更新してキューに追加します
if new_cost < dist[v]:
dist[v] = new_cost
heapq.heappush(pq, (new_cost, v))
# 交差点 1 から 交差点 N までの最小コストを出力します
print(dist[N])
if __name__ == "__main__":
solve()
This editorial was generated by gemini-3-flash-thinking.