Pythonで残りが昇順になるように削除すべき最短部分配列の長さを求める方法
配列 arr が与えられます。この配列から連続した部分配列(サブアレイ)をひとつ取り除いたとき、残りの要素が非減少順(昇順)に並ぶようにしたいとします。このとき、削除すべき最短の部分配列の長さを求めましょう。
たとえば、入力が arr = [10,20,30,100,40,20,30,50] の場合、答えは 3 になります。[100, 40, 20] という長さ3の部分配列を削除すると、残りは [10,20,30,30,50] となり、すべての要素が非減少順に整列するからです。
解法の考え方
この問題は、次の手順で解くことができます。
n:= 配列arrのサイズarr:= 先頭に 0 を挿入し、末尾に無限大(float("inf"))を挿入して番兵(センチネル値)を置くA、B:= 空のリストを2つ用意するp:= 1、q:= 配列のサイズ − 2(番兵を除いた実データの範囲)M:= 0(残せる要素数の最大値)p <= qの間、以下を繰り返しますarr[p-1] <= arr[p]の場合:arr[p]をAの末尾に追加し、pを 1 増やす(左側からできるだけ延ばす)- それ以外で
arr[q] <= arr[q+1]の場合:arr[q]をBの末尾に追加し、Aの末尾にBの末尾より大きい要素がある間それらを取り除いてから、qを 1 減らす - どちらの条件も満たさない場合はループを抜ける
M:=Mと(Aのサイズ +Bのサイズ)の大きい方で更新する
- 最後に
n - M(= 削除が必要な最小の長さ)を返す
ポイントは、左側(A)と右側(B)からそれぞれ非減少の並びを構築しながら、両者がうまくつながるように調整していく点です。先頭と末尾に番兵を置くことで、配列の端における境界条件の扱いがシンプルになります。
Pythonでの実装例
def solve(arr):
n = len(arr)
arr = [0] + arr + [float("inf")]
A, B = [], []
p, q = 1, len(arr) - 2
M = 0
while p <= q:
if arr[p-1] <= arr[p]:
A.append(arr[p])
p += 1
elif arr[q] <= arr[q+1]:
B.append(arr[q])
while A and A[-1] > B[-1]:
A.pop()
q -= 1
else:
break
M = max(M, len(A) + len(B))
return n - M
arr = [10,20,30,100,40,20,30,50]
print(solve(arr))入力
[10,20,30,100,40,20,30,50]
出力
3
この結果から、長さ3の部分配列 [100, 40, 20] を削除すればよいことが正しく判定できていることがわかります。
-
Pythonで配列(リスト)の合計を求める方法をわかりやすく解説
この記事では、配列(リスト)の合計値を求めるという問題に対して、Pythonでの解決策とアプローチをわかりやすく解説します。 問題の定義 配列が入力として与えられたとき、その配列に含まれるすべての要素の合計を計算することを目標とします。 例えば、[1, 2, 3, 4, 5] という配列が与えられた場合、出力は 15 になります。 アプローチ1:ループを使った素朴な方法(総当たり法) 最も基本的な方法は、リストを先頭から順に走査し、各要素を合計用の変数に加算していくやり方です。手順は以下の通りです。 合計を格納する変数を 0 で初期化します。 for ループでリストの各要素を取り出し、順番に
-
Pythonで挿入ソート(Insertion Sort)を実装する方法:アルゴリズムとサンプルコードを徹底解説
この記事では、Python 3.x(およびそれ以前のバージョン)における挿入ソートの実装方法について詳しく解説します。挿入ソートは、トランプの手札を整理するイメージに近い、直感的で理解しやすいソートアルゴリズムです。挿入ソートのアルゴリズム挿入ソートは以下の手順で動作します。入力要素を順番に走査し、各反復ごとにソート済みの配列部分を少しずつ拡張していきます。現在注目している要素(キー)を、ソート済み部分の中で最も大きい値と比較します。キーがその値より大きければ、要素は元の位置のまま次の要素へ進みます。そうでなければ、ソート済み配列内の正しい位置を探し出し、そこへ移動させます。具体的には、ソート