幅優先探索 (BFS) で 知らせが 全員に 届くまでの 分を数える
座席の
海外で定番の問題リスト 4 本には入っていません
問題
教室のgrid で
0は空いている席です 1は、まだ 知らせを 知らない 人が 座っている席です 2は、知らせを 知っている 人が 座っている席です
1 分たつごとに、
- 始めから
知らない 人が いなければ 0 を返します - 行の
数と 列の 数は、 どちらも 1 以上 10 以下です。 どの 行も 同じ長さです - 関数の
中で gridを書き換えてかまいません
入力と出力の例
例 1
左下の
例 2
右上の
例 3
知っている
面接での聞かれ方
面接では、
- 「グリッドに
新鮮な オレンジと 腐った オレンジが あり、 毎分、 腐った オレンジの 隣が 腐ります。 全部腐るまでの 分を 返してください。 無理なら -1 です」 - 1 つずつ
始めの 点から 探す 書き方を 出すと、 「始めの 点が いくつも ある とき、 まとめて 1 回で 探せませんか?」と 聞かれます - 「始めから
新鮮な 物が 無い ときは?」と、 端の 答えを 聞かれます
始めに
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、
関わる学び
グラフ探索は、
まず 考える手順
段 1
1
段 2
知らせは
段 3
段ごとに、
段 4
伝えた2 に
解答
Python
Python
STEPS = ((1, 0), (-1, 0),
(0, 1), (0, -1))
def spread_minutes(grid):
h = len(grid)
w = len(grid[0])
now = []
rest = 0
for r in range(h):
for c in range(w):
v = grid[r][c]
if v == 2:
now += [(r, c)]
elif v == 1:
rest += 1
minutes = 0
while now and rest:
nxt = []
for r, c in now:
for y, x in around(
grid, r, c):
grid[y][x] = 2
rest -= 1
nxt += [(y, x)]
now = nxt
minutes += 1
if rest:
return -1
return minutes
def around(grid, r, c):
# 上下左右の、知らない人の席
h = len(grid)
w = len(grid[0])
out = []
for dy, dx in STEPS:
y = r + dy
x = c + dx
if y < 0 or y >= h:
continue
if x < 0 or x >= w:
continue
if grid[y][x] == 1:
out.append((y, x))
return outJavaScript
JavaScript
function spreadMinutes(grid) {
const h = grid.length;
const w = grid[0].length;
let now = [];
let rest = 0;
grid.forEach((row, r) => {
row.forEach((v, c) => {
if (v === 2) {
now.push([r, c]);
} else if (v === 1) {
rest++;
}
});
});
let minutes = 0;
while (now.length && rest) {
const next = [];
for (const [r, c] of now) {
for (const [y, x] of [
[r + 1, c], [r - 1, c],
[r, c + 1], [r, c - 1],
]) {
const v = grid[y]?.[x];
if (v !== 1) {
continue;
}
grid[y][x] = 2;
rest--;
next.push([y, x]);
}
}
now = next;
minutes++;
}
return rest ? -1 : minutes;
}Go
Go
var steps = [][2]int{
{1, 0}, {-1, 0},
{0, 1}, {0, -1},
}
func spreadMinutes(
grid [][]int,
) int {
h := len(grid)
w := len(grid[0])
now := [][2]int{}
rest := 0
for r := 0; r < h; r++ {
for c := 0; c < w; c++ {
switch grid[r][c] {
case 2:
now = append(now,
[2]int{r, c})
case 1:
rest++
}
}
}
minutes := 0
for len(now) > 0 && rest > 0 {
next := [][2]int{}
for _, p := range now {
for _, d := range steps {
y := p[0] + d[0]
x := p[1] + d[1]
if y < 0 || y >= h ||
x < 0 || x >= w ||
grid[y][x] != 1 {
continue
}
grid[y][x] = 2
rest--
next = append(next,
[2]int{y, x})
}
}
now = next
minutes++
}
if rest > 0 {
return -1
}
return minutes
}JavaScript の grid[y]?.[x] は、undefined にundefined です。1 ではないので、
計算量
- 時間は O(m × n) です。
m と n は 行と 列の 数です。 どの 席も 段に 入るのは 1 回だけで、 上下左右の 4 つを 見るだけです - 使う
記憶は O(m × n) です。 1 つの 段に 入る 席の数です
よく ある間違い
- 知っている
人を 1 人ず つ始めに して探す。 人ごとに 盤全体を 探すと 遅く、 別々の 人からの 分を あとで 合わせる 手間も 出ます。 全員を 同時に 最初の 段に入れます - 最後の
段の あとにも 1 分足す。 次の 段が 空に なった あとで 分を 足すと、 1 分多くなります。 例 1 では 5 を 返してしまいます。 知らない 人が 0 に なったら止めます - 始めから
知らない 人が いないのに 1 分足す。 繰り返しの 条件に 「知らない人がいる」を 入れておけば、 0 を そのまま返せます - 伝えた
人を 書き換えずに 段に入れる。 同じ 人が 何度も 段に 入り、 知らない 人の 数が 負になります
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「始めに
知っている 人を 全員、 最初の 段として 列に 入れ、 知らない 人の 数を 数えます。 幅優先で 段ごとに 広げ、 隣の 知らない 人を 知る 側に して次の 段に 入れ、 1 段で 1 分を 足します。 知らない 人が 0 に なったら その 分を、 段が 尽きても 残っていれば -1 を 返します。 どの 席も 1 回だけ段に 入るので、 時間は O(m × n) です。」
書き終えたら、
「例 3 では、
両端の 2 人が 最初の 段です。 1 分目に 2 番目と 4 番目の 人へ 届き、 知らない 人は 1 人です。 2 分目に まん中の 人へ 届いて 0 人に なるので、 2 を 返します。」