D - 配達ルートの最適化 / Optimizing Delivery Routes Editorial by admin
Claude 4.6 Opus (Thinking)概要
速度規制のある道路の所要時間を2倍に変更した上で、地点 \(1\) から地点 \(N\) への最短経路を求める問題です。典型的な重み付きグラフの最短経路問題であり、ダイクストラ法で解くことができます。
考察
問題の整理
- \(N\) 個の地点と \(M\) 本の双方向道路からなるグラフが与えられる
- \(K\) 本の道路は速度規制を受けており、所要時間が通常の 2倍 になる
- 地点 \(1\) から地点 \(N\) への最短時間を求める
重要な気づき
この問題では、速度規制によって辺の重みが変わるだけで、グラフの構造自体は変わりません。つまり、辺の重みを適切に設定してから、通常の最短経路問題を解けばよい ということになります。
具体的には、道路 \(i\) の重みを次のように設定します:
- 道路 \(i\) が速度規制対象(\(i \in \{C_1, C_2, \ldots, C_K\}\))なら、重みは \(2W_i\)
- そうでなければ、重みは \(W_i\)
なぜダイクストラ法か
辺の重みはすべて正(\(W_i \geq 1\) なので \(2W_i\) も正)であるため、ダイクストラ法が適用できます。BFS は重みなしグラフ向けなので使えません。ベルマン–フォード法でも正しい答えは得られますが、計算量が \(O(NM)\) となり、\(N, M\) が最大 \(2 \times 10^5\) の場合は間に合わない可能性があります。
アルゴリズム
- 入力を読み取り、各道路の情報を保存する
- 速度規制対象の道路番号を集合(set)に格納する
- 隣接リストを構築する。各道路について、速度規制対象なら重み \(2W_i\)、そうでなければ重み \(W_i\) として辺を追加する
- ダイクストラ法で地点 \(1\) から各地点への最短距離を求める
- 優先度付きキュー(最小ヒープ)を使い、距離が最小の頂点から順に確定させる
- 地点 \(N\) に到達した時点で早期終了できる(最適化)
- 地点 \(N\) への最短距離を出力する。到達不可能なら \(-1\) を出力する
具体例
例えば、\(N=3, M=3\) で以下の道路があるとします: - 道路1: 地点1–2、所要時間 \(3\) - 道路2: 地点2–3、所要時間 \(5\) - 道路3: 地点1–3、所要時間 \(10\)
速度規制対象が道路3(\(K=1, C_1=3\))の場合、道路3の所要時間は \(20\) になります。すると、地点1→地点2→地点3のルート(所要時間 \(3+5=8\))が最短となります。
計算量
- 時間計算量: \(O((N + M) \log N)\)
- ダイクストラ法の標準的な計算量です。各辺について最大1回のヒープ操作(\(O(\log N)\))を行います。
- 空間計算量: \(O(N + M)\)
- 隣接リスト、距離配列、優先度付きキューの分です。
実装のポイント
速度規制の判定を高速に行う: 速度規制対象の道路番号を
setに格納することで、\(O(1)\) で判定できます。リストの線形探索(\(O(K)\))を毎回行うと遅くなります。入力の高速読み取り: \(N, M\) が大きいため、Python では
sys.stdin.buffer.read()でまとめて読み取ることで入力処理を高速化しています。早期終了: ダイクストラ法で地点 \(N\) をヒープから取り出した時点で、その距離が最短であることが保証されるため、即座に答えを出力して終了できます。
\(K = 0\) の場合: 速度規制対象の道路がない場合、最終行の入力が存在しない点に注意が必要です(コード中では
if K > 0で分岐)。ソースコード
import heapq
import sys
def main():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
K = int(input_data[idx]); idx += 1
edges = []
for i in range(M):
U = int(input_data[idx]); idx += 1
V = int(input_data[idx]); idx += 1
W = int(input_data[idx]); idx += 1
edges.append((U, V, W))
restricted = set()
if K > 0:
for j in range(K):
C = int(input_data[idx]); idx += 1
restricted.add(C)
# Build adjacency list
adj = [[] for _ in range(N + 1)]
for i in range(M):
U, V, W = edges[i]
road_id = i + 1
if road_id in restricted:
w = 2 * W
else:
w = W
adj[U].append((V, w))
adj[V].append((U, w))
# Dijkstra
INF = float('inf')
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:
print(d)
return
for v, w in adj[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heapq.heappush(pq, (nd, v))
print(-1 if dist[N] == INF else dist[N])
main()
この解説は claude4.6opus-thinking によって生成されました。
posted:
last update: