公式
B - 山道ハイキング / Mountain Trail Hiking 解説 by admin
Claude 4.6 Opus (Thinking)概要
山道の \(N\) 個の地点を、1つ飛ばしまたは隣接する地点へ進みながら地点 \(1\) から地点 \(N\) まで移動するとき、上り坂で発生する疲労の合計を最小化する問題です。動的計画法(DP)で解くことができます。
考察
問題の整理
- 地点 \(i\) からは地点 \(i+1\) または地点 \(i+2\) に進める
- 上り(\(A_j > A_i\))のときだけ疲労 \(A_j - A_i\) が加算される
- 下りや平坦な移動では疲労は \(0\)
重要な気づき
各地点に到達したときの最小疲労さえ分かれば、そこから先の遷移を計算できます。これはまさに DP(動的計画法)の典型パターンです。
地点 \(i\) に到達する方法は、地点 \(i-1\) から来るか、地点 \(i-2\) から来るかの高々2通りしかありません。したがって、各地点について定数回の計算で最小疲労を求められます。
具体例
例えば \(A = [10, 30, 15, 40]\)(\(N = 4\))の場合:
- 地点1: 疲労 \(0\)(出発地)
- 地点2: 地点1から → \(\max(30-10, 0) = 20\)
- 地点3: 地点1から → \(\max(15-10, 0) = 5\)、地点2から → \(20 + \max(15-30, 0) = 20\)。最小は \(5\)
- 地点4: 地点2から → \(20 + \max(40-30, 0) = 30\)、地点3から → \(5 + \max(40-15, 0) = 30\)。最小は \(30\)
1つ飛ばしで深い谷を避けることで、疲労を抑えられる場合があることがポイントです。
アルゴリズム
動的計画法を使います。
- \(dp[i]\) を「地点 \(1\) から地点 \(i+1\) まで移動するときの疲労の最小値」と定義する(0-indexed)
- 初期値:\(dp[0] = 0\)(出発地点では疲労 \(0\))
- 遷移:各地点 \(i\) について、地点 \(i+1\) と地点 \(i+2\) への移動を考える $\(dp[j] = \min(dp[j],\; dp[i] + \max(A_j - A_i,\; 0))\)\( ただし \)j \in {i+1, i+2}\( かつ \)j < N$
- 答えは \(dp[N-1]\)
計算量
- 時間計算量: \(O(N)\) — 各地点で高々2つの遷移先を調べるだけ
- 空間計算量: \(O(N)\) — DP 配列のサイズ
実装のポイント
DP 配列を十分大きな値(\(\infty\))で初期化し、\(dp[0] = 0\) とするのが定石です。
遷移先のインデックスが \(N\) を超えないように範囲チェック(
if j < N)を忘れないようにしましょう。sys.stdin.buffer.read()を使って入力を一括読み込みすることで、Python でも \(N = 5 \times 10^5\) 程度の入力を高速に処理できます。コストの計算
max(A[j] - A[i], 0)は、上りのときだけ標高差を加算し、下りでは \(0\) にするという問題文の条件を簡潔に表現しています。ソースコード
import sys
def main():
input_data = sys.stdin.buffer.read().split()
N = int(input_data[0])
A = [int(input_data[i + 1]) for i in range(N)]
INF = float('inf')
dp = [INF] * N
dp[0] = 0
for i in range(N):
if dp[i] == INF:
continue
for j in (i + 1, i + 2):
if j < N:
cost = max(A[j] - A[i], 0)
if dp[j] > dp[i] + cost:
dp[j] = dp[i] + cost
print(dp[N - 1])
main()
この解説は claude4.6opus-thinking によって生成されました。