Pythonプログラム:2つのリストを厳密に増加列にするための最小スワップ回数を求める
同じ長さを持つ2つの数値リストAとBが与えられているとします。ここで、A[i]とB[i]の値を入れ替える(スワップする)という操作を実行できるものとします。このとき、両方のリストを厳密に増加する列(各要素が直前の要素より必ず大きい列)にするために必要な最小の操作回数を求めるのがこの問題です。
例えば、入力が A = [2, 8, 7, 10]、B = [2, 4, 9, 10] の場合、出力は 1 になります。これは、Aの「7」とBの「9」を一度だけ入れ替えることで、A = [2, 8, 9, 10]、B = [2, 4, 7, 10] となり、どちらのリストも厳密に増加する列になるからです。
解決アプローチ:動的計画法(DP)
この問題は、再帰的な動的計画法を用いて効率的に解くことができます。以下の手順に従います。
- 関数
dp()を定義します。引数として現在のインデックスiと、直前の位置でスワップを行ったかどうかを示すフラグprev_swappedを受け取ります。 - Aのサイズがiと等しい場合(すべての要素を処理し終えた場合):0を返します。
- iが0の場合(先頭の要素の場合):
min(dp(i + 1, False), 1 + dp(i + 1, True))を返します。つまり、「スワップしない場合」と「スワップする場合」のうち、操作回数が少ない方を選択します。 - それ以外の場合:
prev_A := A[i - 1]、prev_B := B[i - 1]を設定します。prev_swappedがTrueの場合、prev_Aとprev_Bの値を入れ替えます(実際に前のステップでスワップされていた値を反映するため)。A[i] <= prev_AまたはB[i] <= prev_Bの場合:現状のままでは厳密な増加列にならないため、現在の位置でスワップするしか選択肢がありません。1 + dp(i + 1, True)を返します。- それ以外の場合:
ans := dp(i + 1, False)(スワップしない場合の結果)A[i] > prev_BかつB[i] > prev_Aの場合、スワップしても成立するため、ans = min(ans, 1 + dp(i + 1, True))としてより小さい方を採用します。ansを返します。
- メインメソッドから
dp(0, False)を呼び出し、その戻り値を結果として返します。
それでは、以下の実装例を見て理解を深めましょう。
サンプルコード
class Solution: def solve(self, A, B): def dp(i=0, prev_swapped=False): if len(A) == i: return 0 elif i == 0: return min(dp(i + 1), 1 + dp(i + 1, True)) else: prev_A = A[i - 1] prev_B = B[i - 1] if prev_swapped: prev_A, prev_B = prev_B, prev_A if A[i] <= prev_A or B[i] <= prev_B: return 1 + dp(i + 1, True) else: ans = dp(i + 1) if A[i] > prev_B and B[i] > prev_A: ans = min(ans, 1 + dp(i + 1, True)) return ans return dp() ob = Solution() A = [2, 8, 7, 10] B = [2, 4, 9, 10] print(ob.solve(A, B))
入力
[2, 8, 7, 10], [2, 4, 9, 10]
出力
1
このアルゴリズムでは、各位置において「スワップする・しない」の2つの選択肢を探索しながら、直前の状態(スワップ済みかどうか)を考慮することで、全体として最小の操作回数を正しく求めることができます。
-
Pythonで1つの数を別の数に変換するのに必要な最小操作回数を求めるプログラム
問題の概要 2つの整数 start と end(start < end)が与えられます。次の2種類の操作のみを使って start を end に変換するとき、必要な操作の最小回数を求めるプログラムを作成しましょう。 数値に 1 を加える(インクリメント) 数値に 2 を掛ける 例として、start = 5、end = 11 の場合を考えます。5 に 2 を掛けて 10 とし、そこへ 1 を加えれば 11 になるため、答えは 2 回となります。 解き方のアプローチ この問題は、start から順に操作を試すよりも、end から逆算していく貪欲法(グリーディ法)が有効です。end が偶
-
Pythonで文字列tを別の文字列sの部分文字列にするために必要な最小操作回数を求めるプログラム
問題の概要2つの文字列 s と t が与えられたとき、t を s の部分文字列にするために必要な最小の操作回数を求めます。ここでいう1回の操作とは、「s 内の任意の位置を選び、その位置の文字を任意の別の文字に変更する」ことを指します。例えば、入力が s = abbpqr、t = bbxy の場合、出力は 2 になります。これは、s の部分文字列 bbpq に着目し、p を x に、q を y に変更することで t = bbxy と一致させられるためです。解法のアプローチこの問題はスライディングウィンドウ(全開始位置の走査)を使うことで簡単に解けます。s の中で長さ k(= t の長さ)に等しい