Pythonで色付きボールの販売による最大利益を求めるプログラムの作成方法
問題の概要
ここに inventory という配列があるとします。inventory[i] は、i 番目の色のボールの初期在庫数を表します。さらに、顧客が購入したいボールの総数を表す値 orders も与えられます。ボールはどの順序でも販売でき、顧客はどんな色のボールでも受け入れます。
このボールの価値には特別なルールがあります。各色のボールの価値は、「その色のボールが現在インベントリに何個残っているか」に等しくなります。たとえば、現在青いボールが6個ある場合、最初の1個は価格6で売れ、残りが5個になるため、次の青いボールは価格5で売れることになります。この条件のもとで、orders 個のボールを売った後に得られる合計価値の最大値を求めます。答えが非常に大きくなる可能性があるため、10^9 + 7 で割った余りを返してください。
入力例
たとえば、入力が inventory = [5,7]、orders = 6 の場合、出力は 31 になります。これは、1つ目の色のボールを2回(価格 5, 4)、2つ目の色のボールを4回(価格 7, 6, 5, 4)売ることができ、合計利益が 5+4+7+6+5+4 = 31 となるからです。
解法のアプローチ
この問題を効率的に解くには、二分探索(バイナリサーチ)を活用します。高い価格のボールから順に売っていくのが最適戦略であるため、「これ以下の価格では売らない」というしきい値を二分探索で特定します。手順は以下の通りです。
low := 0、high := 10000000と初期化します。low < highの間、次を繰り返します。mid := (low + high) // 2(商)とします。s := 0とします。inventory内の各要素iについて、i > midならばs := s + (i - mid)とします。s > ordersならばlow := mid + 1、そうでなければhigh := midとします。
再度
mid := (low + high) // 2を計算します。ans := 0とします。inventory内の各要素iについて、i > midならば次を行います。ans := ans + i*(i+1)//2 - mid*(mid+1)//2(連続する整数の総和の公式を利用)orders := orders - (i - mid)
ans := ans + orders * midを加算します。ans mod (10^9 + 7)を返します。
Pythonでの実装例
理解を深めるために、以下の実装を見てみましょう。
def solve(inventory, orders):
low = 0
high = 10000000
# 売却しきい値を二分探索で求める
while low < high:
mid = (low + high) // 2
s = 0
for i in inventory:
if i > mid:
s += i - mid
if s > orders:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
ans = 0
for i in inventory:
if i > mid:
ans += i * (i + 1) // 2 - mid * (mid + 1) // 2
orders -= i - mid
ans += orders * mid
return ans % (10**9 + 7)
inventory = [5, 7]
orders = 6
print(solve(inventory, orders))
入力
[5, 7], 6
出力
31
アルゴリズムのポイント
この解法の鍵となるのは、次の2点です。
二分探索によるしきい値の特定: 「mid 以上の在庫を持つ色から、mid を超える分だけ売る」という戦略で販売可能な個数を数え、orders と比較しながらしきい値を絞り込みます。
等差数列の和の公式: 在庫 i 個から mid 個まで売るときの売上は i + (i-1) + … + (mid+1) となり、これは i*(i+1)/2 − mid*(mid+1)/2 として定数時間で計算できます。
計算量は、二分探索の各反復で在庫配列を走査するため、O(n log m)(n は色の数、m は在庫の最大値)となります。貪欲法的な発想と二分探索を組み合わせることで、大量の注文にも高速に対応できるのが特徴です。
-
Pythonで株式の保有と売却により得られる最大利益を求めるプログラム
時系列順に並べられた企業の株価リスト nums が与えられたとします。1日につき最大1株まで購入でき、複数の銘柄を同時に保有することや、任意のタイミングで売却することが可能です。この条件のもとで、獲得できる利益の最大値を求めてください。たとえば、入力が nums = [3, 4, 7, 3, 5] の場合、出力は 9 になります。具体的には、価格が 3 と 4 のときにそれぞれ 1 株ずつ購入し、価格が 7 になった時点で両方とも売却します。その後、再び価格が 3 のときに購入し、価格が 5 のときに売却します。合計利益は (7 − 3) + (7 − 4) + (5 − 3) = 9 です。
-
Pythonで制約付きの建物の最大高さを求めるプログラム
問題の概要整数 n と制約リスト restrictions が与えられたとします。私たちは都市に n 棟の新しい建物を一列に建てようとしていますが、高さに関するいくつかの制限があります。建物には左から順に 1 から n までの番号が付けられており、各制約は restrictions[i] = (id_i, max_height_i) の形式で表され、「id_i 番の建物の高さは max_height_i 以下でなければならない」ことを意味します。建物の高さに関する都市の規則は以下のとおりです。各建物の高さは 0 以上でなければなりません。1 番の建物(最初の建物)の高さは必ず 0 です。隣接す