POJ 3487 The Stable Marriage Problem
題目大意
有個男人向個女人求婚。每個男人會從他最喜歡的女人開始一個一個地求婚。每個女人對男人也有不同的喜歡程度,當有某個女人有更喜歡的求婚者來求婚時,她會拒絕掉之前來求婚的男人。求最終哪些男人和女人會結成一對。
分析
這是一個穩定匹配的模板題
考慮這樣一個算法:
我們進行第一輪的求婚:每個男人會選取名單上的第一個女人,並向她們求婚。
這時會出現兩種情況:
- 一位女士還沒有收到任何求婚信息,那麼她一定會接受當前的男人;
- 一位女士收到了其他人的求婚信息,那麼她會從這些人中選出最喜歡的人,接受他並拒絕剩下的人。
不難發現第一輪下來一定會有男人求婚沒有成功。於是我們再做一輪。
我們發現這輪裏參加的男人會從名單上沒有拒絕他的人選出一個他最喜歡的求婚,那麼也會發生如上所說的兩種情況。
對於第二輪沒有成功的人,我們讓他們再來一次。這樣重複多輪以後,所有人一定能夠配對。
這個算法的正確性?
但這樣看來,這個算法似乎就像個無底洞,只要有一個男人沒有成功那麼就會死循環。
隨着輪數的增加,我們可以發現每個男人只要按照名單上的順序挨個求婚,那麼他一定會找到一個願意接受他的女人。假若有一個男人到最後都沒有成功,那麼他一定是向所有的女人都求過婚的。但一個女生只要接受了求婚,那麼她就一直會有男朋友。又由於左右兩部分點數相同,根據 Hall 定理,一定存在一個完美匹配。這樣就與有一個人沒有女朋友相矛盾,故每個男人都能夠找到一個女人。
並且隨着輪數的增加,男人所能夠找到的女人是越來越差的,而女人所接受的男人一定是越來越好的。
綜上所述,我們就可以用一個隊列來維護求婚失敗或者沒有求婚的男人,每次取出一個節點更新答案即可。
這樣我們就得到了用以求解穩定匹配問題的算法——Gale-Shapley算法。
參考代碼
#include <map>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn = 100;
int N;
map<char, int> mp;
queue<int> q;
int pref[Maxn + 5][Maxn + 5];
int order[Maxn + 5][Maxn + 5];
int ptr[Maxn + 5];
int matchx[Maxn + 5], matchy[Maxn + 5];
inline void clear() {
while(!q.empty()) q.pop();
memset(matchx, 0, sizeof matchx);
memset(matchy, 0, sizeof matchy);
memset(pref, 0, sizeof pref);
memset(order, 0, sizeof order);
memset(ptr, 0, sizeof ptr);
mp.clear();
}
void GaleShapley() {
while(!q.empty()) {
int x = q.front();
q.pop();
int y = pref[x][ptr[x]++];
if(!matchx[y] || order[y][x] < order[y][matchx[y]]) {
int t = matchx[y];
if(y) matchy[t] = 0, q.push(t);
matchx[y] = x, matchy[x] = y;
} else q.push(x);
}
for(char ch = 'a'; ch < 'z'; ch++)
if(mp[ch]) printf("%c %c\n", ch, matchy[mp[ch]] + 'A' - 1);
}
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int _;
scanf("%d", &_);
while(_--) {
clear();
scanf("%d", &N);
for(int i = 1; i <= N; i++) {
char ch[100];
scanf("%s", ch);
mp[ch[0]] = i;
}
for(int i = 1; i <= N; i++) {
char ch[100];
scanf("%s", ch);
mp[ch[0]] = i;
}
for(int i = 1; i <= N; i++) {
char inp[100];
scanf("%s", inp);
for(int j = 2; inp[j] != '\0'; j++)
pref[mp[inp[0]]][j - 1] = mp[inp[j]];
ptr[mp[inp[0]]] = 1;
q.push(mp[inp[0]]);
}
for(int i = 1; i <= N; i++) {
char inp[100];
scanf("%s", inp);
for(int j = 2; inp[j] != '\0'; j++)
order[mp[inp[0]]][mp[inp[j]]] = j - 1;
}
GaleShapley();
if(_) puts("");
}
return 0;
}