D - アルバイトのシフト割り当て / Part-Time Job Shift Assignment 解説 by admin
GPT 5.2 High概要
必要スキル \(H_i\) と売上 \(S_i\) を持つ \(N\) 日から、各アルバイトのスキル \(P_j\) で出勤可能な日(\(H_i \le P_j\))に 全員を重複なく割り当てられるかを判定し、可能なら 売上合計を最大化します。
考察
各アルバイトは「自分のスキル以下の必要スキルの日なら出勤できる」ので、アルバイト \(j\) は条件 \(H_i \le P_j\) を満たす日だけ選べます。また、1日につき1人までです。
重要な気づき
- 「割り当て可能かどうか」と「最大利益」を同時に考える必要があります。
- アルバイトのスキルが低い人ほど選べる日が少ないため、スキルの低い順に処理するのが自然です。
- あるアルバイト \(p\) を処理するとき、その時点で割り当て可能な日(\(H_i \le p\))の中から 売上 \(S_i\) が最大のものを選ぶのが得です。
素朴な方法が難しい理由
- 全ての割り当てを探索すると組合せ爆発します(最大で \(N\) から \(M\) を選んで並べるような規模)。
- 二部マッチングをそのまま組むと、辺が「\(H_i \le P_j\)」で非常に多くなりうるため(最悪 \(NM\))、制約 \(2 \times 10^5\) では現実的ではありません。
どう解決するか
「スキル順に見て、その時点で可能な日の中から最大売上を選ぶ」という貪欲を、効率よく実装します。 - 「その時点で可能な日」の集合を管理するために 優先度付きキュー(ヒープ) を使います。
アルゴリズム
- 営業日を \((H_i, S_i)\) のペアとして用意し、必要スキル \(H_i\) の昇順にソートする。
- アルバイトのスキル \(P_j\) も昇順にソートする。
- 小さいスキルのアルバイトから順に処理する:
- そのアルバイトのスキルを \(p\) とする。
- まだ見ていない営業日のうち、\(H_i \le p\) を満たすものをすべてヒープに追加する(売上 \(S_i\) を入れる)。
- ヒープが空なら、このアルバイトが入れる日がもう無いので 割り当て不可能(
-1)。 - ヒープから最大の \(S_i\) を取り出し、その日をこのアルバイトに割り当て、合計に加える。
- 全アルバイトを処理できたら合計売上を出力する。
なぜこの貪欲が正しいか(直感)
スキルの低いアルバイトは選択肢が少ないため、後回しにすると詰みやすいです。
一方、あるスキル \(p\) の時点で選べる日(\(H_i \le p\))は、将来(より高スキルのアルバイト)でも必ず選べます。
よって「今選べる中で一番売上が大きい日を今取る」ことで、将来の選択肢を不必要に狭めず、合計売上を最大化できます。
(簡単な例)
- 日:\((H,S)=(1,10),(2,100)\)
- アルバイト:\(P=(1,2)\)
\(P=1\) のとき選べるのは \(H=1\) の日だけなのでそれを取る(10)。
次に \(P=2\) は残った \(H=2\) の日(100)を取れる。合計110が最大です。
計算量
- 時間計算量: ソートに \(O(N\log N + M\log M)\)、各日を1回pushし各アルバイトで1回popするのでヒープ操作が \(O((N+M)\log N)\)。まとめて \(O((N+M)\log N)\)。
- 空間計算量: 営業日配列とヒープで最大 \(O(N)\)。
実装のポイント
Python の
heapqは最小ヒープなので、最大値を取りたい場合は \(-S_i\) を入れて取り出すときに符号を戻します。営業日側は \(H_i\) の昇順に並べ、ポインタ
idxを進めながら「\(H_i \le p\) になった日」をヒープに追加していくと、全体を1回ずつしか走査しません。各アルバイトでヒープが空になった瞬間に
-1を出して終了すれば、判定も最大化も同時に行えます。ソースコード
import sys
import heapq
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
it = iter(data)
N = next(it)
M = next(it)
days = []
for _ in range(N):
h = next(it)
s = next(it)
days.append((h, s))
days.sort()
P = [next(it) for _ in range(M)]
P.sort()
heap = []
idx = 0
total = 0
for p in P:
while idx < N and days[idx][0] <= p:
heapq.heappush(heap, -days[idx][1])
idx += 1
if not heap:
print(-1)
return
total += -heapq.heappop(heap)
print(total)
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
投稿日時:
最終更新: