ベルマン・フォード法で 乗り継ぎ k 回までの いちばん安い 運賃を求める
都市を
海外で定番の問題リスト 4 本には入っていません
問題
n か所のflights をflights[i] = [a, b, p] は、
都市 src からdst へ、k 回まで (つまり便を k + 1 本まで
- n は 2 以上 100 以下、
運賃 p は 1 以上 10000 以下の整数です - 同じ a から
同じ b への 便は 2 本ありません。 自分の 都市へ 戻る 便も ありません srcとdstは違う 都市です。 kは 0 以上、n より 小さい整数です
入力と出力の例
例 1
0 から 3 へは、
例 2
例 1 と
例 3
0 から 2 へは 1 で
面接での聞かれ方
面接では、
- 「都市と
便の 運賃が 渡されます。 乗り継ぎ k 回以内で src から dst へ 行く いちばん安い 運賃を 返してください」 - ダイクストラ法で
書くと 「乗り継ぎの 上限が ある とき、 いちばん安い 所から 確定して よいですか?」と 聞かれます - 「計算量は? 便の
数と k の どちらで 決まりますか?」と 聞かれます
乗り継ぎの
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、
関わる学び
グラフ探索は、
まず 考える手順
段 1
ふつうの
段 2
「便を i 本までsrc だけが 0 で、[a, b, p] にk + 1 回繰り返すと、k + 1 本までの
段 3
1 回の
段 4
k + 1 回のdst の
解答
Python
Python
def cheapest_fare(
flights, n, src, dst, k
):
inf = float("inf")
cost = [inf] * n
cost[src] = 0
for _ in range(k + 1):
nxt = cost[:]
for a, b, p in flights:
c = cost[a] + p
if c < nxt[b]:
nxt[b] = c
cost = nxt
if cost[dst] == inf:
return -1
return cost[dst]JavaScript
JavaScript
function cheapestFare(
flights, n, src, dst, k,
) {
let cost = Array(n)
.fill(Infinity);
cost[src] = 0;
for (let i = 0; i <= k; i++) {
const next = [...cost];
for (const f of flights) {
const [a, b, p] = f;
const c = cost[a] + p;
if (c < next[b]) {
next[b] = c;
}
}
cost = next;
}
if (cost[dst] === Infinity) {
return -1;
}
return cost[dst];
}Go
Go
func cheapestFare(
flights [][]int,
n, src, dst, k int,
) int {
// 届かない印。運賃の合計より大きい
const inf = 1 << 60
cost := make([]int, n)
for i := range cost {
cost[i] = inf
}
cost[src] = 0
for i := 0; i <= k; i++ {
next := append(
[]int{}, cost...)
for _, f := range flights {
a, b := f[0], f[1]
if cost[a] == inf {
continue
}
c := cost[a] + f[2]
if c < next[b] {
next[b] = c
}
}
cost = next
}
if cost[dst] == inf {
return -1
}
return cost[dst]
}Python と JavaScript は、
計算量
- 時間は O(k × E) です。
E は 便の 数です。 k + 1 回の 繰り返しで、 毎回 全部の 便を 見ます。 表を 写すのに 毎回 O(n) かかりますが、 合わせても O(k × (n + E)) です - 使う
記憶は O(n) です。 前の 回と 新しい 回の 2 つの表です
よく ある間違い
- 表を
その場で 書き換える。 1 回の 中で 便を 何本も 進めてしまい、 乗り継ぎの 上限を 超えた 運賃が 出ます。 例 1 の 便の 順で その場で 書き換えると、 1 回目の うちに 0 から 1、 2、 3 と 進み、 k = 0 でも 250 になります - 繰り返しを k 回にする。
乗り継ぎ k 回は 便 k + 1 本です。 k 回で 止めると、 例 1 で 1 回乗り継ぐ道を 見ずに、 直行便の 700 を返します - ふつうの
ダイクストラ法で、 都市ごとに 1 回だけ確定する。 安くて 乗り継ぎの 多い道で 確定すると、 乗り継ぎの 少ない 道を 見なくなり、 行けるのに -1 を 返すことがあります - Go で
印の 大きな 数に 運賃を足す。 印から 書き換えが 起きない よう、 届いていない 都市からの 便は 飛ばします
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「便を i 本まで
使った ときの 各都市への 最安の 運賃を 表にし、 src だけ 0 で 始めます。 k + 1 回、 前の 回の 表から 全部の 便で 次の 表を 緩めます。 前の 回を 読んで 新しい 表に 書くので、 1 回で 進むのは 便 1 本だけです。 最後に dst が 届かないままなら -1 を 返します。 時間は O(k × E) です。」
書き終えたら、
「例 1 で、
1 回目は 0 から 出る 便だけが 効き、 1 が 100、 3 が 700 です。 2 回目は、 1 から 2 へ 150、 1 から 3 へ 300 で 700 を 書き換えます。 k は 1 なので ここで 止め、 3 の 300 を 返します。」