Python
 Computer >> コンピューター >  >> プログラミング >> Python

与えられた操作でリストを減らしながら、Pythonで残る最小の数を求めるプログラム

問題の概要

正の整数からなるリスト nums が与えられます。ここで、次のような操作を考えます。

  • リストから任意の2つの値 a と b(a ≤ b)を取り除く
  • a < b が成り立つ場合、その差 b − a をリストへ挿し戻す

この操作は何回でも実行できます。最終的にリストに残せる最小の数を求めてください。なお、リストが空になった場合は 0 を返します。

入出力例

たとえば nums = [2, 4, 5] の場合、答えは 1 になります。まず 4 と 5 を選んで差の 1 を戻せばリストは [2, 1] となり、続いて 2 と 1 を選べば [1] だけが残ります。

解き方のアプローチ

この問題は、各要素を「足す側」と「引く側」に振り分けたときの差の絶対値の最小値を求める、いわゆる部分和(パーティション)問題と同じ構造を持っています。合計を s とすると、ある部分集合の合計を n としたとき、最終的な残りは s − 2n で表せます。つまり「s − 2n が負にならない範囲で n をできるだけ大きく取る」ことが目標になります。

以下の手順で再帰的に解きます。

  • s := nums の全要素の合計
  • 関数 f(i, s) を定義する
  • i が nums のサイズ以上なら s を返す(ベースケース)
  • n := nums[i]
  • s − 2 × n < 0 の場合、この要素を引く側に使えないため f(i + 1, s) を返す
  • それ以外は f(i + 1, s − 2 × n) と f(i + 1, s) の小さい方を返す
  • メイン処理では f(0, s) を返す

Pythonでの実装例

それでは、実際のコードを見てみましょう。

def solve(nums):
    s = sum(nums)

    def f(i, s):
        if i >= len(nums):
            return s
        n = nums[i]
        if s - 2 * n < 0:
            return f(i + 1, s)
        return min(f(i + 1, s - 2 * n), f(i + 1, s))

    return f(0, s)

nums = [2, 4, 5]
print(solve(nums))

入力

[2, 4, 5]

出力

1

計算量と高速化のポイント

この再帰解法は各要素ごとに「使う / 使わない」の2択を試すため、時間計算量は O(2n)、再帰の深さによる空間計算量は O(n) となります。要素数が多い場合は、メモ化(動的計画法)やビット演算を使った部分和DPに置き換えることで大幅に高速化できます。

  1. Pythonでリスト内の最小値を見つける方法を解説

    この記事では、リストの中から最小の数値を見つける方法について、具体的なサンプルコードとともに詳しく解説します。問題の概要問題: 数値のリストが与えられたとき、その中に含まれる最も小さい数値を画面に表示すること。この問題を解くアプローチは主に2つあります。ひとつは sort() メソッドを使ってリストを昇順に並べ替え、先頭の要素(インデックス0)を取得する方法。もうひとつは、Pythonに標準で用意されている組み込み関数 min() を使う方法です。それぞれ順番に見ていきましょう。方法1:sort()メソッドで並べ替えて最小値を取得するまずはリストを昇順にソートし、先頭の要素を取り出す方法です。

  2. 指定した範囲内の乱数を生成してリストに保存するPythonプログラム

    このプログラムでは、3つのユーザー入力変数を使用します。1つ目は乱数の開始値、2つ目は終了値、3つ目は生成して表示する乱数の個数です。 Pythonには乱数を生成するための標準モジュール random が用意されています。本記事では、その中でも指定した範囲内の整数乱数を返す randint() 関数を使用します。randint() は2つの引数(開始値と終了値)を受け取り、両端の値を含む範囲からランダムな整数を1つ返します。 実行例 n :: 10 s :: 20 e :: 40 [20, 35, 32, 27, 24, 35, 28, 31, 20, 28] アルゴリズム Random