Please sign in first.
Official
D - 最安経路 / Cheapest Route Editorial by admin
GPT 5.2 High概要
各道路の移動コストが「両端都市の人口の積 \(M_a \times M_b\)」で与えられるグラフ上で、都市 \(1\) からいずれかの空港都市までの最小通行料(最短距離)を求める問題です。
考察
- 都市を頂点、道路を辺とみなすと、都市 \(a\) から隣接都市 \(b\) へ移動するコストは辺の重み \(w(a,b)=M_a \times M_b\) です。道路は双方向なので無向グラフですが、コストはどちら向きでも同じ(積なので対称)です。
- 求めたいのは「都市 \(1\) から、空港都市集合 \(\{E_1,\dots,E_P\}\) のどれかに到達するまでの最小コスト」=「始点から複数の終点のうち最短のもの」です。
素朴案がダメな理由
- 全経路を探索(DFSで全パス列挙など)すると、同じ都市・道路を何度でも通れるため経路数が爆発します。
- BFSは辺重みがすべて同じときしか最短距離を保証できません。本問題では \(M_a \times M_b\) が辺ごとに異なり得るため、BFSでは誤答になります。
解決の方針
- 辺重みがすべて非負(\(M_i \ge 1\) なので積も正)なので、ダイクストラ法で最短距離を求められます。
- 終点が複数ある場合でも、ダイクストラ法で「最短距離が確定した頂点」を順に取り出していく性質を使えば、空港都市を最初に確定した時点でそれが答えになります(それより安い空港は存在しない)。
例:空港が複数あっても、優先度付きキューから取り出される距離は単調増加なので、「最初に取り出された空港」は全空港の中で最小距離です。
アルゴリズム
- 空港都市かどうかを配列
is_airportで管理する。 - もし都市 \(1\) が空港なら、移動不要で答えは \(0\)。
- 距離配列
distを用意し、dist[1]=0、それ以外は \(\infty\)。 - 優先度付きキュー(最小ヒープ)に
(0, 1)を入れてダイクストラ法を開始。 - ヒープから
(d, u)を取り出す。- 取り出した
dがdist[u]と違うなら古い情報なので無視。 - もし
uが空港都市なら、dが最小通行料なので出力して終了。
- 取り出した
uの全隣接頂点vについて、遷移コスト \(M_u \times M_v\) を加えた $\( nd = d + M_u \times M_v \)$ で緩和し、nd < dist[v]なら更新してヒープへ入れる。- 必ずどれかの空港へ到達可能なので、どこかで終了する。
計算量
- 時間計算量: \(O((N+K)\log N)\)
(ダイクストラ法:各辺の緩和が最大 \(2K\) 回、ヒープ操作が \(\log N\)) - 空間計算量: \(O(N+K)\)
(隣接リスト、距離配列、空港フラグ、ヒープ)
実装のポイント
「空港に着いたら即終了」が重要です。ダイクストラ法では最小距離の頂点から確定していくため、最初に確定した空港が答えになります。
Pythonでは入力が大きいので、
sys.stdin.buffer.read()を使った高速入力にしています。隣接リストも
head/to/nxtの配列形式(C言語風)にして、\(N,K \le 2\times 10^5\) でも高速に走るようにしています。距離の最大値は大きくなり得るため(経路長により積が多数回加算)、
INF=10**30のように十分大きい値を使います。ソースコード
import sys
import heapq
def main():
data = sys.stdin.buffer.read()
ndata = len(data)
ptr = 0
def next_int():
nonlocal ptr
while ptr < ndata and data[ptr] <= 32:
ptr += 1
sign = 1
if ptr < ndata and data[ptr] == 45:
sign = -1
ptr += 1
num = 0
while ptr < ndata:
c = data[ptr]
if c <= 32:
break
num = num * 10 + (c - 48)
ptr += 1
return num * sign
N = next_int()
K = next_int()
P = next_int()
M = [0] * (N + 1)
for i in range(1, N + 1):
M[i] = next_int()
head = [-1] * (N + 1)
to = [0] * (2 * K)
nxt = [0] * (2 * K)
ei = 0
for _ in range(K):
u = next_int()
v = next_int()
to[ei] = v
nxt[ei] = head[u]
head[u] = ei
ei += 1
to[ei] = u
nxt[ei] = head[v]
head[v] = ei
ei += 1
is_airport = [False] * (N + 1)
for _ in range(P):
e = next_int()
is_airport[e] = True
if is_airport[1]:
print(0)
return
INF = 10**30
dist = [INF] * (N + 1)
dist[1] = 0
hq = [(0, 1)]
while hq:
d, u = heapq.heappop(hq)
if d != dist[u]:
continue
if is_airport[u]:
print(d)
return
mu = M[u]
e = head[u]
while e != -1:
v = to[e]
nd = d + mu * M[v]
if nd < dist[v]:
dist[v] = nd
heapq.heappush(hq, (nd, v))
e = nxt[e]
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
posted:
last update: