問題
$N$ 人の男性と $N$ 人の女性がいる。各男性は $N$ 人の女性全員に対する選好リストを、各女性も $N$ 人の男性全員に対する選好リストを持つ。
マッチングが安定であるとは、「男性 $m$ と女性 $w$ が互いに現在のパートナーよりも相手を好んでいる」という不安定なペアが存在しないことをいう。男性がプロポーズする Gale-Shapley アルゴリズムを用いて、男性全員にとって最も好ましい安定マッチング(男性最適安定マッチング)を求めよ。
入力形式
N
m_pref_1
:
m_pref_N
w_pref_1
:
w_pref_N
m_pref_i: 男性iの選好リスト(女性番号を好きな順に並べたN個の整数)。w_pref_i: 女性iの選好リスト(男性番号を好きな順に並べたN個の整数)。
制約
$1 \le N \le 2000$
各選好リストは $1..N$ の順列
入出力例
入力例1
3
1 2 3
2 1 3
1 2 3
2 1 3
1 2 3
1 2 3
出力例1
1 2 3
出力のi番目は「男性iとマッチした女性の番号」。この例では男性1-女性1、男性2-女性2、男性3-女性3が男性最適安定マッチング。
概念図
ヒント(段階的開示)
ヒント1: 方向性
貪欲に「一番好きな相手同士をペアにする」だけでは安定性が壊れる。一度成立したペアを仮の状態として扱い、より良い提案が来たら破棄する仕組みを考えよ。
ヒント2: アプローチ
未婚の男性が選好リストの上から順に、まだプロポーズしていない女性にプロポーズしていく。女性は「今の仮の相手」と「新しい提案者」を比べ、選好リストで順位が高い方を残し、もう一方を未婚に戻す。全員が結ばれるまで繰り返す。
ヒント3: 誘導(コード骨格)
next_proposal = [0]*(n+1)
woman_partner = [0]*(n+1) # 0 = 未婚
free_men = list(range(n, 0, -1))
while free_men:
m = free_men.pop()
w = man_pref[m][next_proposal[m]]
next_proposal[m] += 1
if woman_partner[w] == 0:
woman_partner[w] = m
elif woman_rank[w][m] < woman_rank[w][woman_partner[w]]:
free_men.append(woman_partner[w])
woman_partner[w] = m
else:
free_men.append(m)
woman_rank[w][m]は女性wにとっての男性mの順位(小さいほど好み)を事前計算しておく。
模範解答 (Python)
import sys
def main():
data = sys.stdin.buffer.read().split()
idx = 0
n = int(data[idx]); idx += 1
man_pref = []
for _ in range(n):
row = [int(data[idx + j]) for j in range(n)]
idx += n
man_pref.append(row)
woman_pref = []
for _ in range(n):
row = [int(data[idx + j]) for j in range(n)]
idx += n
woman_pref.append(row)
woman_rank = [[0] * (n + 1) for _ in range(n + 1)]
for w in range(1, n + 1):
for rank, m in enumerate(woman_pref[w - 1]):
woman_rank[w][m] = rank
next_proposal = [0] * (n + 1)
woman_partner = [0] * (n + 1)
free_men = list(range(n, 0, -1))
while free_men:
m = free_men.pop()
w = man_pref[m - 1][next_proposal[m]]
next_proposal[m] += 1
if woman_partner[w] == 0:
woman_partner[w] = m
elif woman_rank[w][m] < woman_rank[w][woman_partner[w]]:
free_men.append(woman_partner[w])
woman_partner[w] = m
else:
free_men.append(m)
man_partner = [0] * (n + 1)
for w in range(1, n + 1):
man_partner[woman_partner[w]] = w
sys.stdout.write(' '.join(str(man_partner[m]) for m in range(1, n + 1)) + "\n")
main()
計算量: 各男性は最大N回プロポーズするため総プロポーズ回数は $O(N^2)$。女性側の判定は事前計算した順位表で $O(1)$。
Step-by-Step 解説
1順位表の構築
女性側の「男性番号→順位」の逆引き表
女性側の「男性番号→順位」の逆引き表
woman_rankを作り、比較を $O(1)$ にする。2未婚男性のスタック管理
free_menに未婚男性を積み、1人ずつ取り出してプロポーズさせる。3プロポーズと判定
女性が未婚なら即受理。既に相手がいれば
女性が未婚なら即受理。既に相手がいれば
woman_rankで比較し、好みの高い方を残す。4停止性と出力
各男性のプロポーズ回数は高々N回なので必ず停止する。
各男性のプロポーズ回数は高々N回なので必ず停止する。
woman_partnerからman_partnerを逆算する。よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
女性側の好みを毎回index()で線形探索 | 順位表を事前計算していない | woman_rank[w][m]を $O(1)$ で引けるように前処理 |
| 同じ女性に何度もプロポーズする | next_proposalのインクリメント忘れ | プロポーズのたびに必ず+= 1 |
| 女性最適マッチングと勘違い | 問題文の「男性最適」の見落とし | 男性からプロポーズするアルゴリズムを実装 |
| 安定性を「片方だけが好む」で判定 | 不安定ペアの定義の理解不足 | Gale-Shapleyの出力は自動的に安定になることを利用 |
次のステップ
- 発展: 定員が複数人の「病院・研修医マッチング問題」(多対一安定マッチング)への拡張
- 発展: 選好リストが不完全な場合の安定マッチング
- 次回予告: Tonelli-Shanks法(mod p の平方剰余)