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

Pythonでボールの入った袋のペナルティを最小化するプログラム(二分探索による解法)

nums という配列があり、i 番目の要素は nums[i] 個のボールが入った袋を表しているとします。さらに、mx という別の値も与えられます。この mx は、以下の操作を実行できる最大回数を意味します。

  • 任意のボールの入った袋を1つ選び、少なくとも1個ずつのボールが入った2つの新しい袋に分割する。
  • ここで「ペナルティ」とは、すべての袋の中で最も多くのボールが入っている袋のボール数を指します。

私たちの目的は、操作を最大 mx 回まで行った後のペナルティを最小化すること、すなわち実現可能な最小のペナルティを求めることです。

例えば、入力が nums = [4,8,16,4]、mx = 4 の場合、出力は 4 になります。これは次のような手順で操作できるためです。

  1. 初期状態:[4,8,16,4]
  2. 16個のボールが入った袋を、8個ずつの2つの袋に分割 → [4,8,8,8,4]
  3. 8個の袋をそれぞれ4個ずつに分割 → [4,4,4,8,8,4] → [4,4,4,4,4,8,4]
  4. 最終的に [4,4,4,4,4,4,4,4]

こうしてすべての袋が4個になり、最小ペナルティは4となります。

解法のアプローチ

この問題は二分探索を使って効率的に解けます。基本的な発想は、「ペナルティを target 以下にするために必要な分割回数が mx 回以内に収まるか?」という判定関数を作り、target の候補値を二分探索で絞り込んでいくというものです。

helper() 関数の手順

  • 引数として target と mx を受け取る関数 helper() を定義します。
  • target が 0 の場合は mx + 1 を返します(判定を必ず失敗させるため)。
  • count := 0 と初期化します。
  • nums の各要素 num に対して、count := count + (num - 1) // target を加算していきます。これは各袋を target 以下にするために必要な分割回数です。
  • 最後に count <= mx を返します。

メイン処理の手順

  • left := max(sum(nums) // (len(nums) + mx), 1) —— 理論上の下限値
  • right := max(nums) —— 袋の中の最大ボール数
  • left < right の間、以下を繰り返します。
    • mid := (left + right) // 2
    • helper(mid, mx) が真であれば right := mid
    • そうでなければ left := mid + 1
  • 最後に left を返します。

実装例

理解を深めるために、以下の実装を見てみましょう。

def helper(target, mx):
    if target == 0:
        return mx + 1
    count = 0
    for num in nums:
        count += (num - 1) // target
    return count <= mx

def solve(nums, mx):
    left, right = max(sum(nums) // (len(nums) + mx), 1), max(nums)
    while left < right:
        mid = (left + right) // 2
        if helper(mid, mx):
            right = mid
        else:
            left = mid + 1
    return left

nums = [4,8,16,4]
mx = 4
print(solve(nums, mx))

入力

[4,8,16,4], 4

出力

4
  1. Pythonで木棒を切断する最小コストを求めるプログラム|区間DPによる効率的な解法

    問題概要 整数 n と配列 cuts が与えられます。長さ n 単位の木棒があり、両端には 0 から n までの目盛りが付けられています。cuts[i] は棒を切断できる位置を表します。切断はどのような順序でも実行できますが、1回の切断にかかるコストは「その瞬間に切断する木棒の長さ」であり、全体のコストはすべての切断コストの合計です。合計コストが最小になるように切断順序を選んだときの最小コストを求めます。 具体例 n = 7、cuts = [5, 1, 4, 3] の場合、答えは 16 になります。たとえば切断順序を [3, 5, 1, 4] とすると、以下のように進みます。 まず長さ 7

  2. Pythonで全ての点を接続するための最小コストを求めるプログラム

    問題の概要(x, y) の形式で表される複数の点が格納された配列 points があるとします。2つの点 (xi, yi) と (xj, yj) を接続するコストは、それらの間のマンハッタン距離として定義されます。マンハッタン距離は次の式で計算できます。|xi − xj| + |yi − yj|この問題では、すべての点を接続するために必要な最小のコストを求める必要があります。入力例points = [(0,0), (3,3), (2,10), (6,3), (8,0)]この場合、出力は 22 になります。これは、各辺の距離がそれぞれ 6 + 5 + 3 + 8 = 22 となるように点同士を接