ヒープで水位の上がる池を対角まで渡れるいちばん早い時刻を求める

高さの違う石が並んだ池で、水位が 1 ずつ上がるとき、左上から右下まで水の上を渡れるいちばん早い時刻を求めます。道の「いちばん高い石」を小さくする最短経路を、ヒープで低い所から広げて解く練習です。

海外で定番の問題リスト 4 本には入っていません

問題

n 行 n 列のマスに区切った池があり、各マスに石が置いてあります。石の高さを 2 次元の並び grid で受け取ります。時刻 t には水位が t になり、高さが t 以下の石は全部水に沈みます。

泳ぐ人は、沈んだマスどうしなら、上下左右で隣り合うマスへ一瞬で泳いで移れます。時刻 0 に左上のマス (0, 0) にいて、右下のマス (n - 1, n - 1) に着ける、いちばん早い時刻を返す関数を書いてください。泳いでいるマスも着くマスも、その時刻に沈んでいる (高さが水位以下である) 必要があります。

  • n は 1 以上 50 以下です
  • 石の高さは 0 から n² - 1 までの整数で、どの 2 つも違います

入力と出力の例

例 1

左上の 0 から、右の 1、下の 5、下の 3、右の 4 と泳ぐ道で、いちばん高い石は 5 です。左の 6 を通る道や、右の 7 と 8 を通る道は、もっと待つことになります。右下の石は 4 ですが、途中の 5 が沈む時刻 5 まで着けません。

grid = [[0, 1, 7], [6, 5, 8], [2, 3, 4]]
5

例 2

右下の石が 3 なので、少なくとも時刻 3 までは着けません。時刻 3 には 4 マスとも沈んでいます。

grid = [[0, 1], [2, 3]]
3

例 3

まん中の 0 の周りに低い石があっても、左上の 7 が沈むまでは泳ぎ出せません。左上の 7 から右の 1、まん中の 0、下の 2、右下の 3 と泳げる時刻 7 が答えです。

grid = [[7, 1, 8], [6, 0, 5], [4, 2, 3]]
7

面接での聞かれ方

面接では、短く次のように渡されることが多いです。

  • 「グリッドの各マスに高さがあり、時刻 t には高さ t 以下のマスを泳げます。左上から右下へ着く最小の時刻を返してください」
  • ダイクストラ法で書くと「道の長さではなく道のいちばん高い所を小さくするのに、なぜダイクストラ法が使えるのですか?」と聞かれます
  • 「時刻で二分探索して、その時刻に着けるかを幅優先探索で調べる書き方もできますか?」と聞かれることがあります

泳ぎ始める左上のマスも沈んでいる必要があるかを最初に確かめておくと、例 3 のような所で 1 つ早い答えを返す間違いを防げます。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、この型の要点を表とコードでまとめています。

グラフ探索は、つながりを近い順か奥からたどります。迷路、最短の手数、講座を受ける順のような問題が同じ形です。

main.py
def swim_time(grid: list[list[int]]) -> int:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(swim_time([[0, 1, 7], [6, 5, 8], [2, 3, 4]]))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します