動的計画法で低いマスから順に埋め、グリッドの上り続ける最長の道を求める

高さの書かれたグリッドを、上下左右の隣へ高さが上がり続けるように進むとき、通れるマスの数の最大を求めます。低いマスから順に「そこで終わる道の長さ」を決めていく練習です。

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

問題

山の地図が、整数のグリッド heights で与えられます。heights[r][c] は r 行 c 列のマスの高さです。好きなマスから歩き始め、上下左右の隣のマスへ、今より高いマスにだけ進めます。通れるマスの数のいちばん大きい値を返す関数を書いてください。

  • 斜めには進めません。グリッドの外へも出られません
  • 同じ高さのマスへは進めません。高さが真に上がるときだけ進めます
  • 1 マスだけでも道として数え、長さは 1 です
  • 行の数と列の数は、どちらも 1 以上 200 以下です。高さは 0 以上 2^31 未満の整数です

入力と出力の例

例 1

左下の 1 から上へ 2、3 と進み、右へ 4、5、下へ 6、7、左へ 8、まん中の 9 と、渦を巻いて全部のマスを通れます。

heights = [[3, 4, 5], [2, 9, 6], [1, 8, 7]]
9

例 2

どのマスも同じ高さなので、どこへも進めません。

heights = [[5, 5], [5, 5]]
1

例 3

上の段のまん中の 2 から下の 3 へ進み、左の 6、上の 7 と進む 4 マスがいちばん長い道です。2 から下の 3、右の 8、上の 9 と進んでも 4 マスです。

heights = [[7, 2, 9], [6, 3, 8]]
4

面接での聞かれ方

面接では、グリッドの探索と動的計画を組み合わせる問題として出されます。

  • 「上下左右に、値が上がる方へだけ進めます。最も長い道の長さは?」
  • まず全部のマスから深さ優先で歩く書き方を出すと、「200 × 200 で間に合いますか?」と聞かれます
  • 「訪ねたマスの印は要りますか?」と聞かれることもあります (上がり続けるので、同じマスへ戻ることはありません)

上がり続ける決まりのおかげで輪ができないことに気づけるかが見どころです。

ヒント

まだ開いていません

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

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「動的計画法 (DP)」に、この型の要点を表とコードでまとめています。

動的計画法は、途中の答えを覚えておいて、同じ計算をやり直さない書き方です。漸化式で言い直せる問題が多いです。

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

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

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

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


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

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