Pythonで最大k回の隣接スワップ後に得られる最小の整数を求めるプログラム
非常に大きな整数を表す文字列 num と、整数 k が与えられているとします。隣接する2つの桁を入れ替える操作を最大 k 回まで行えるとき、実現できる最小の値を求める必要があります。
たとえば、入力が num = "5432"、k = 4 の場合、出力は 2453 になります。最初の数は 5432 ですが、1回目の交換で 4532、続いて 4523、次に 4253、そして最終的に 2453 となります。
アルゴリズムの考え方
この問題は貪欲法(グリーディ法)で効率的に解けます。左端の桁から順に、「残りの交換回数 k で移動できる範囲内に存在する最も小さい数字」をその位置へ引き寄せます。これを繰り返すことで、上位の桁から順にできるだけ小さな数字を配置し、全体として最小の整数を作り上げます。
解決手順
- min_num := num の各桁を昇順にソートした文字列
- i := 0、to_find := 0 で初期化
- num が min_num と等しくなく、k > 0 かつ i < num の長さである間、以下を繰り返す
- indx := インデックス i 以降で to_find が出現する位置
- indx が -1 でない間、以下を繰り返す
- indx − i ≤ k の場合:
- num := num[0〜i−1] + num[indx] + num[i〜indx−1] + num[indx+1〜末尾]
- k := k − (indx − i)
- i := i + 1
- to_find := 0
- indx を再び検索
- それ以外の場合はループを抜ける
- indx − i ≤ k の場合:
- to_find := to_find + 1
- num を返す
実装例
理解を深めるために、以下のPython実装を見てみましょう。
def solve(num, k):
min_num = sorted(list(num))
min_num = ''.join(min_num)
i = 0
to_find = 0
while num != min_num and k > 0 and i < len(num):
indx = num.find(str(to_find), i)
while indx != -1:
if indx - i <= k:
num = num[:i] + num[indx] + num[i:indx] + num[indx+1:]
k -= (indx - i)
i += 1
to_find = 0
indx = num.find(str(to_find), i)
else:
break
to_find += 1
return num
num = "5432"
k = 4
print(solve(num, k))
入力
"5432", 4
出力
2453
計算量
最悪の場合、各桁の配置ごとに文字列内の検索と再構築が必要になるため、時間計算量は O(n²) 程度になります(n は num の桁数)。必要な作業用変数以外に追加のデータ構造を持たないため、空間計算量は O(n) です。
-
Pythonで色のマージ後に残る最小個数を求めるプログラム
問題概要 赤(R)、緑(G)、青(B)の3種類の色からなるリストを考えます。隣り合う異なる2つの色は、残りの「第3の色」1個に変換(マージ)できます。この変換を好きな順序で何度でも繰り返してよいとき、最終的に残る要素数の最小値を求めるのがこの問題です。 たとえば入力が colors = [G, R, G, B, R] の場合、次のように変換を進めることで最終的に1個まで減らせます。したがって出力は 1 となります。 解き方のアプローチ 一見すると状態探索が必要そうな問題ですが、実はXOR(排他的論理和)を使ったシンプルな判定だけで答えが求まります。手順は以下の通りです。 n := 色リス
-
Pythonで数の因子の最小合計を求めるプログラム|素因数分解の考え方
本記事では、与えられた整数について、積が元の数と等しくなる因子の組み合わせの中から合計が最小となる値を求める方法を、Pythonのコード例とともに解説します。 問題定義 入力として1つの整数が与えられます。この数を複数の因子の積として表したとき、因子の合計が最小になるケースを求めてください。 すべての因子の組み合わせを網羅的に調べて合計を比較する方法もありますが、実はもっとシンプルで効率的なアプローチが存在します。 考え方:素因数の合計が最小になる 鍵となるのは次の性質です。積が一定の値になるとき、因子の合計が最小になるのは、すべての因子を素数まで分解した場合(素因数分解した場合)です。