壊れた 段を よけて 階段を 上る方法の数
- 動的計画法
- 問題 15
- やや難しい
- 目安 45 分
1 段、
本番の
入力は
問題
工場の
0 段目から N 段目まで
入力の形式
N M
b_1
b_2
...
b_M1 行目に
出力の形式
上り方の
制約
- N は 1 以上 1000000 以下の整数です
- M は 0 以上 100000 以下で、
N - 1 以下の整数です - b_i は 1 以上 N - 1 以下の
整数で、 b_1 < b_2 < ... < b_M を 満たします (地面と、 いちばん上の 段は 壊れていません)
入力と出力の例
例 1
5 段の
入力
5 1 3
出力
5
例 2
4 段の
入力
4 0
出力
7
ヒント
まだ開いていません
付箋は 3 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3答えに近い
全部の
先に
解答
ways[i] を
Python
Python
import sys
MOD = 10**9 + 7
data = sys.stdin.read().split()
n, m = int(data[0]), int(data[1])
broken = [False] * (n + 1)
for i in range(m):
broken[int(data[2 + i])] = True
ways = [0] * (n + 1)
ways[0] = 1
for i in range(1, n + 1):
if broken[i]:
continue
total = ways[i - 1]
if i >= 2:
total += ways[i - 2]
if i >= 3:
total += ways[i - 3]
ways[i] = total % MOD
print(ways[n])JavaScript
JavaScript
const data = require("fs").readFileSync(0, "utf8").split(/\s+/).filter(Boolean).map(Number);
const MOD = 1000000007;
const n = data[0];
const m = data[1];
const broken = new Array(n + 1).fill(false);
for (let i = 0; i < m; i++) broken[data[2 + i]] = true;
const ways = new Array(n + 1).fill(0);
ways[0] = 1;
for (let i = 1; i <= n; i++) {
if (broken[i]) continue;
let total = ways[i - 1];
if (i >= 2) total += ways[i - 2];
if (i >= 3) total += ways[i - 3];
ways[i] = total % MOD;
}
console.log(ways[n]);計算量
時間は O(N + M) です。
遅い
よく ある間違い
- 最後に 1 回だけ 1000000007 で
割る。 Python では 答えは 合いますが、 途中の 数が 何十万桁にもなって 足し算が 遅くなります。 JavaScript では 数が 大きすぎて 正しい 値を 表せません。 足すたびに 割った 余りにします - ways[0] を 0 に
してしまい、 全部が 0 に なる。 地面に 立っている 状態は 1 通りと数えます - 壊れた
段を 「その 段から 先へ 進めない」だけに して、 その 段までの 数を 0 にし忘れる - i が 1 や 2 の
ときに ways[i - 3] を 読み、 Python では 負の 番号が 配列の 後ろを 指して、 間違った 値を 足してしまう - 壊れた
段の 番号を、 ずっと list のまま 「in」で 調べる。 毎回 O(M) かかるので、 N 段ぶんでは 遅くなります。 印の 配列か set にします
解答を動かした版 Python 3.12.14Node.js 22.23.4確認日 2026/10/31