Pythonで顧客の列に順番どおりチケットを販売できるか判定する方法(紙幣のお釣り問題)
問題の概要
キュー(列)に並んでいる顧客がそれぞれ持っているルピー紙幣を表す配列 notes があると仮定しましょう。彼らは全員、50ルピーのチケットを1枚ずつ購入しようとしています。使用可能な紙幣は [50, 100, 200] の3種類です。
売り手は最初、手元にお金を一切持っていません(所持金0ルピー)。この状態で、並んでいる順番どおりにすべての人へチケットを販売できるかどうかを判定する必要があります。途中でお釣りを用意できなくなった場合、その時点で販売は失敗となります。
動作例
たとえば、入力が notes = [50, 50, 100, 100] の場合、答えは True になります。
最初の2人の顧客はちょうど50ルピーを支払うため、お釣りは不要です。この時点で手元には50ルピー紙幣が2枚あります。続く2人が100ルピー紙幣で支払う際には、それぞれ50ルピー紙幣をお釣りとして渡せるため、全員に順番どおりチケットを販売することができます。
解法の考え方
この問題は貪欲法(グリーディ法)で解けます。各紙幣の所持枚数を記録しながら、支払われた紙幣ごとにお釣りを返せるかを確認していきます。手順は以下のとおりです。
freq: 各紙幣の所持枚数を管理する空のマップ(辞書)を用意します。i: 先頭から順に処理する位置を表すインデックスです。iがnotesの長さ未満である限り、以下を繰り返します。notes[i]が 50 の場合:お釣りは不要なので、freq[50]を1増やします。notes[i]が 100 の場合:freq[100]を1増やします。freq[50]が0であればループを抜けて失敗とし、そうでなければfreq[50]を1減らしてお釣りを渡します。notes[i]が 200 の場合:freq[100] > 0かつfreq[50] > 0なら、100ルピー紙幣1枚と50ルピー紙幣1枚でお釣り150ルピーを作ります。- そうでなく
freq[50] >= 3なら、50ルピー紙幣3枚でお釣り150ルピーを作ります。 - いずれも不可能な場合はループを抜けて失敗とします。
- 処理が終わったら
iを1増やします。
- 最後に
iがnotesの長さと一致していれば全員への販売に成功したのでTrueを返し、途中で抜けた場合はFalseを返します。
なお、200ルピーを受け取った際に「100ルピー+50ルピー」のお釣りを優先するのは、50ルピー紙幣が後続の100ルピー支払いのお釣りにも必要となる貴重な紙幣だからです。この優先順位の付け方が貪欲法の重要なポイントになります。
実装例
理解を深めるために、以下のPythonコードを見てみましょう。
from collections import defaultdict
def solve(notes):
freq = defaultdict(int)
i = 0
while i < len(notes):
if notes[i] == 50:
freq[50] += 1
elif notes[i] == 100:
freq[100] += 1
if freq[50] == 0:
break
freq[50] -= 1
else:
if freq[100] > 0 and freq[50] > 0:
freq[100] -= 1
freq[50] -= 1
elif freq[50] >= 3:
freq[50] -= 3
else:
break
i += 1
if i == len(notes):
return True
return False
notes = [50, 50, 100, 100]
print(solve(notes))
入力
[50, 50, 100, 100]
出力
True
まとめ
このアルゴリズムは配列を一度だけ走査するため、時間計算量は O(n) です。また、管理対象の紙幣の種類が固定されているため、空間計算量は O(1) で済みます。お釣りの組み合わせの優先順位(100ルピー+50ルピーを優先)を正しく選ぶことが、この問題を効率よく解く鍵となります。
-
Pythonのさまざまなグラフでデータを可視化する方法
Pythonには、初心者でも手軽に扱えるデータ可視化ライブラリが数多く用意されています。これらのライブラリは、小規模なデータセットから大規模なデータセットまで幅広く対応できる点が大きな魅力です。データ可視化で特に広く使われているPythonライブラリは以下のとおりです。MatplotlibPandasPlotlySeaborn本記事では、1つの固定データセットを題材に、さまざまな種類のグラフを描画することで、データを多角的に分析する方法を解説します。今回使用するのは、インドの2019年における人口推計(単位:千人)に関する次のデータセットです。国・地域年バリアント値インド2019Medium(
-
Pythonで文字列が特定のサブ文字列(プレフィックス)で始まるかどうかを確認する方法
startswith()メソッドを使う方法 Pythonでは、文字列クラスに標準で用意されているstartswith()メソッドを使うことで、文字列が指定したプレフィックス(接頭辞)で始まるかどうかを簡単に確認できます。このメソッドは、検索したいプレフィックス文字列を引数として受け取り、文字列オブジェクトに対して呼び出します。戻り値は真偽値(True / False)です。 >>> hello world.startswith(hell) True >>> Harry Potter.startswith(Harr) True >>> hel