動的計画法で 低い マスから 順に 埋め、 グリッドの 上り続ける 最長の 道を求める
高さの
海外で定番の問題リスト 4 本には入っていません
問題
山のheights でheights[r][c] は r 行 c 列の
- 斜めには
進めません。 グリッドの 外へも 出られません - 同じ
高さの マスへは 進めません。 高さが 真に 上がる ときだけ進めます - 1 マスだけでも
道として 数え、 長さは 1 です - 行の
数と 列の 数は、 どちらも 1 以上 200 以下です。 高さは 0 以上 2^31 未満の整数です
入力と出力の例
例 1
左下の 1 から
例 2
どの
例 3
上の
面接での聞かれ方
面接では、
- 「上下左右に、
値が 上がる方へだけ 進めます。 最も 長い道の 長さは?」 - まず
全部の マスから 深さ優先で 歩く 書き方を 出すと、 「200 × 200 で 間に 合いますか?」と 聞かれます - 「訪ねた
マスの 印は 要りますか?」と 聞かれる こともあります (上がり続けるので、 同じ マスへ 戻る ことは ありません)
上がり続ける
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「動的計画法 (DP)」に、
関わる学び
動的計画法は、
まず 考える手順
段 1
どの
段 2
「そのbest[r][c] とbest[r][c] は、best の
段 3
best[r][c] を
段 4
上がり続けるbest の
解答
Python
Python
STEPS = ((1, 0), (-1, 0),
(0, 1), (0, -1))
def lower(g, r, c):
for dr, dc in STEPS:
y, x = r + dr, c + dc
if y < 0 or x < 0:
continue
if y >= len(g):
continue
if x >= len(g[0]):
continue
if g[y][x] < g[r][c]:
yield y, x
def longest_rise(heights):
g = heights
rows = range(len(g))
cols = range(len(g[0]))
cells = sorted(
(g[r][c], r, c)
for r in rows
for c in cols
)
best = [[1] * len(cols)
for _ in rows]
answer = 0
for _, r, c in cells:
low = lower(g, r, c)
for y, x in low:
n = best[y][x] + 1
if n > best[r][c]:
best[r][c] = n
if best[r][c] > answer:
answer = best[r][c]
return answerJavaScript
JavaScript
const STEPS = [
[1, 0], [-1, 0],
[0, 1], [0, -1],
];
function lower(g, r, c) {
const out = [];
for (const d of STEPS) {
const y = r + d[0];
const x = c + d[1];
const row = g[y];
if (row === undefined) {
continue;
}
if (row[x] < g[r][c]) {
out.push([y, x]);
}
}
return out;
}
function longestRise(heights) {
const g = heights;
const cells = [];
g.forEach((row, r) => {
row.forEach((h, c) => {
cells.push([h, r, c]);
});
});
cells.sort(
(p, q) => p[0] - q[0],
);
const best = g.map(
(row) => row.map(() => 1),
);
let answer = 0;
for (const cell of cells) {
const [, r, c] = cell;
const low = lower(g, r, c);
for (const [y, x] of low) {
const n = best[y][x] + 1;
if (n > best[r][c]) {
best[r][c] = n;
}
}
if (best[r][c] > answer) {
answer = best[r][c];
}
}
return answer;
}Go
Go
import "sort"
var steps = [][2]int{
{1, 0}, {-1, 0},
{0, 1}, {0, -1},
}
func lower(
g [][]int, r, c int,
) [][2]int {
out := [][2]int{}
for _, s := range steps {
y, x := r+s[0], c+s[1]
if y < 0 || y >= len(g) {
continue
}
if x < 0 || x >= len(g[y]) {
continue
}
if g[y][x] < g[r][c] {
p := [2]int{y, x}
out = append(out, p)
}
}
return out
}
func longestRise(
heights [][]int,
) int {
g := heights
cells := [][2]int{}
best := make([][]int, len(g))
for r := range g {
n := len(g[r])
best[r] = make([]int, n)
for c := range g[r] {
p := [2]int{r, c}
cells = append(cells, p)
best[r][c] = 1
}
}
high := func(i int) int {
p := cells[i]
return g[p[0]][p[1]]
}
less := func(i, j int) bool {
return high(i) < high(j)
}
sort.Slice(cells, less)
answer := 0
for _, p := range cells {
r, c := p[0], p[1]
low := lower(g, r, c)
for _, q := range low {
n := best[q[0]][q[1]] + 1
if n > best[r][c] {
best[r][c] = n
}
}
if best[r][c] > answer {
answer = best[r][c]
}
}
return answer
}lower は、best をcells をbest に 1 をbest にlower は、g[y] が undefined にrow[x] < g[r][c] が undefined とのsort を
計算量
- 時間は O(m × n × log(m × n)) です (m は
行、 n は 列の 数)。 並べ替えが いちばん重く、 その 後は どの マスも 隣 4 つを 1 回ずつ 見るだけです - 使う
記憶は O(m × n) です。 並べた マスの 並びと bestの表を持ちます - 深さ優先と
メモで 書くと、 並べ替えが 要らず 時間は O(m × n) です。 ただし再帰の 深さが 最大で マスの 数に なるので、 言語に よっては 再帰の 上限に 気をつけます
よく ある間違い
- 同じ
高さへ も進めるようにする。 <=で比べると、 例 2 で 同じ マスの 間を 行き来して 答えが 決まらなくなります。 真に 低い マスだけを見ます - 行の
順や列の 順に埋める。 左上から 順に 埋めると、 まだ 決まっていない 低い隣を 1 として 使ってしまいます。 例 1 の 渦では、 左上の 3 を 埋める 時に 下の 2 が まだ 1 のままなので、 3 で 終わる 道を 2 と 数えます。 その 誤りが 先へ 伝わり、 答えは 9 ではなく 6 になります - 訪ねた
印を 付けて 一度きりにする。 深さ優先で 「訪ねた マスには もう 入らない」と すると、 別の 始まりからの 長い道を 見落とします。 印ではなく、 その マスで 終わる (または 始まる ) 道の 長さを覚えます
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「上がり続けるので
道に 輪は できません。 その マスで 終わる 最長の 道は、 低い隣の マスの 最長に 1 を 足した値です。 低い 隣が 先に 決まるよう、 全部の マスを 高さの 順に 並べて 埋めます。 時間は 並べ替えの O(m n log(m n))、 記憶は O(m n) です。」
書き終えたら、
「例 3 で
たどります。 低い順に 2 は 低い隣が 無く 1、 3 は 2 の 隣で 2、 6 は 3 の 隣で 3、 7 は 6 の 隣で 4 です。 8 は 3 の 隣で 3、 9 は 8 の 隣で 4 です。 最大は 4 です。」