B - 最長の登り坂 / Longest Uphill Editorial by admin Editorial for printing
Claude 4.6 Opus (Thinking)概要
数列の中で、値が非減少(広義単調増加)である連続部分列の最大長を求める問題です。
考察
重要な気づき
「登り坂」とは、隣り合う地点の標高を比較したとき、すべて \(A_i \leq A_{i+1}\) が成り立つ連続区間のことです。つまり、途中で標高が下がった瞬間に「登り坂」は途切れます。
素朴なアプローチとその問題点
すべての区間 \((l, r)\) の組を試し、それぞれが登り坂かどうかを判定する方法が考えられます。区間の組は \(O(N^2)\) 通りあり、各区間の判定に最大 \(O(N)\) かかるため、全体で \(O(N^3)\) となり、\(N = 10^5\) では到底間に合いません。
どう解決するか
実は、配列を左から順に1回走査するだけで答えが求められます。ポイントは次の通りです:
- 現在の「登り坂」の長さを変数
cur_lenで管理する - \(A_i \geq A_{i-1}\) なら登り坂が続いているので
cur_lenを \(1\) 増やす - \(A_i < A_{i-1}\) なら登り坂が途切れたので
cur_lenを \(1\) にリセットする - 各ステップで、これまでの最大値
max_lenを更新する
具体例
例えば \(A = [3, 5, 5, 2, 4, 7, 1]\) の場合:
| \(i\) | \(A_i\) | 比較 | cur_len |
max_len |
|---|---|---|---|---|
| 0 | 3 | — | 1 | 1 |
| 1 | 5 | \(5 \geq 3\) ✓ | 2 | 2 |
| 2 | 5 | \(5 \geq 5\) ✓ | 3 | 3 |
| 3 | 2 | \(2 < 5\) ✗ | 1 | 3 |
| 4 | 4 | \(4 \geq 2\) ✓ | 2 | 3 |
| 5 | 7 | \(7 \geq 4\) ✓ | 3 | 3 |
| 6 | 1 | \(1 < 7\) ✗ | 1 | 3 |
答えは \(3\) です(区間 \([3, 5, 5]\) または \([2, 4, 7]\))。
アルゴリズム
max_len = 1、cur_len = 1で初期化する(地点が1個でも登り坂なので最小値は1)。- \(i = 1\) から \(N-1\) まで順に走査する:
- \(A_i \geq A_{i-1}\) なら
cur_lenを \(1\) 増やす。 - そうでなければ
cur_lenを \(1\) にリセットする。 cur_len > max_lenならmax_lenを更新する。
- \(A_i \geq A_{i-1}\) なら
max_lenを出力する。
この手法は「ランレングス的な走査」とも呼ばれ、条件を満たす連続区間の最大長を求める定番のテクニックです。
計算量
- 時間計算量: \(O(N)\)(配列を1回走査するだけ)
- 空間計算量: \(O(N)\)(配列 \(A\) の格納に必要。走査用の変数は \(O(1)\))
実装のポイント
cur_lenとmax_lenの初期値はどちらも \(1\) にします。\(N = 1\) のとき(地点が1個だけのとき)もループに入らず正しく \(1\) を出力できます。条件は \(A_i > A_{i-1}\)(狭義単調増加)ではなく \(A_i \geq A_{i-1}\)(広義単調増加)であることに注意してください。問題文の「非減少」という条件を正確に反映する必要があります。
ソースコード
N = int(input())
A = [int(input()) for _ in range(N)]
max_len = 1
cur_len = 1
for i in range(1, N):
if A[i] >= A[i-1]:
cur_len += 1
else:
cur_len = 1
if cur_len > max_len:
max_len = cur_len
print(max_len)
この解説は claude4.6opus-thinking によって生成されました。
posted:
last update: