什么是穩定(婚姻)匹配問題
這里是百度百科。有N男N女,均為異性戀,每個人都對異性有好感度排序。如何將他們兩兩配對,才能盡可能使結果令每個人都滿意。
當然也有N男M女、多對一的情況,這里先不討論(網上有些大牛寫了論文)。
處理方案
被廣泛認可的算法是由美國數學家 David Gale 和 Lloyd Shapley 於1962年發明的 Gale-Shapley算法,簡稱GS算法。GS算法的思路如下:
先給N男N女從0~N-1分別編號,然后要求每個人寫出他們對異性的好感度排序。為了方便,這里N取4.比如0號男最喜歡3號女,其次是0號女,再次是1號女,最后是2號女,則0號男給出的排序為3 0 1 2.
下圖中展示了好感排序情況(隨機給出):

第一輪,先讓每個男性向他們最喜歡的女性表白(也可以女性表白男性,同理)。例如0號男會先向3號女表白。如果一個女性同時收到了兩個表白,她會選擇接受自己最喜歡的那個男性。如3號女同時收到0號和2號的表白,接受0號。
第一次匹配結束后的情況:
0號男-3號女 1號男-0號女 3號男-1號女
第二輪,由於第一輪時只有2號男落單(慘),這次2號男選擇向自己第二喜歡的1號女表白。1號女接到表白后,會比較現任對象(3號男)和2號男,發現自己更喜歡2號男,於是與3號男分手,投入2號男的懷抱。
第二次匹配結束后的情況:
0號男-3號女 1號男-0號女 2號男-1號女
第三輪,3號男向自己第二喜歡的0號女表白,0號女比較現任配偶(1號男)和3號男,覺得自己還是更喜歡現任配偶,3號男被殘忍拒絕。這次匹配關系沒有發生變化。
第四輪,3號男向自己第三喜歡的2號女表白。2號女還沒有收到過表白,直接接受。
第四次匹配結束后的情況:
0號男-3號女 1號男-0號女 2號男-1號女 3號男-2號女
此時每個人都找到了配偶,匹配結束。
簡單總結一下,每一輪開始時:
①沒有配偶的男性選擇自己好感度最高而且沒表白過的女性表白。
②接到表白的女性比較現任配偶和新追求者的好感度,選擇與好感度更高的男性重新匹配。
GS算法的局限性
從算法過程容易看出,按照“男性表白女性”的策略對女性更有利,因為女性的配偶質量會不斷提升,而男性的配偶質量會不斷變差。相反則對男性有利。不同的表白策略得出的匹配結果很可能是不同的。
GS算法能否保證每個人都有對象
假設有一個男性A落單,由於男女人數相等,則必定還有一個女性B落單。
根據算法,B是被動接受表白,只要收到過一次表白,B就不可能落單。
而如果A沒有配偶,他會一直表白下去,將N個女性全部表白一遍。這個過程中B一定會收到表白,兩人匹配。
上述內容同時可以證明,該算法會在有限次運算后結束(運算次數小於n*n)。
GS算法的結果是否穩定
“穩定”是指:算法結束后,如果再進行一輪表白,一定不會出現男女雙方的選擇都更優的情況。
這一點容易證明。根據算法局限性,再進行表白,男性對新配偶的好感度只會下降。
假設男性A與女性B本應匹配(也就是說現在兩個人分別有自己的配偶,但是從好感度角度來說,他們都更希望彼此形成新的匹配),可以分兩種情況討論:
1.男性A曾對女性B表白,但此時兩人沒有匹配,說明女性B后來遇到了好感度更高的男性,假設不成立。
2.男性A不曾對女性B表白,說明男性A的配偶好感度排在女性B前面,假設不成立。
代碼實現
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<iostream> 5 #include<cstring> 6 #include<string> 7 using namespace std; 8 const int MAXN = 0 + 5; 9 int Num,nowMatch; 10 int Match_A[MAXN], Match_B[MAXN], Preference_A[MAXN][MAXN], Preference_B[MAXN][MAXN], Now[MAXN]; 11 //Match數組記錄匹配情況,Preference[i][1..n]記錄好感排序 12 inline void init() 13 {//讀入、預處理 14 scanf("%d", &Num); 15 for (int i = 0; i < Num; ++i) 16 for (int j = 0; j < Num; ++j) 17 scanf("%d", &Preference_A[i][j]); 18 for (int i = 0; i < Num; ++i) 19 for (int j = 0; j < Num; ++j) 20 scanf("%d", &Preference_B[i][j]); 21 for (int i = 0; i < Num; ++i) 22 Match_A[i] = -1, Match_B[i] = -1; 23 } 24 bool Compare(int a, int b, int Obj) 25 {//比較追求當前的B[i]的兩個人在B[i]處的好感排序 26 int index_a = -1, index_b = -1; 27 for (int i = 0; i < Num; ++i) 28 { 29 if (Preference_B[Obj][i] == a) 30 index_a = i; 31 else if (Preference_B[Obj][i] == b) 32 index_b = i; 33 } 34 if (index_a < index_b) return true; 35 return false; 36 } 37 bool AllMatch() 38 {//判斷是否完成匹配 39 int count = 0; 40 for (int i = 0; i < Num; ++i) 41 if (Match_A[i] != -1) 42 count++; 43 if (count < Num) return false; 44 return true; 45 } 46 void Gale_Shapley() 47 { 48 while (! AllMatch()) 49 {//算法會在所有人完成匹配后終止 50 for (int i = 0; i < Num; i ++) 51 { 52 if (Match_A[i] != -1) continue; 53 nowMatch = Match_B[Preference_A[i][Now[i]]]; 54 //nowMatch是A[i]想要表白的B[i]目前的對象,沒有則為-1 55 if (nowMatch == -1 || Compare(i, nowMatch, Preference_A[i][Now[i]])) 56 {//如果B[i]沒有對象,或者B[i]對A[i]更有好感,則A[i]和B[i]匹配 57 Match_A[i] = Preference_A[i][Now[i]]; 58 Match_A[nowMatch] = -1; 59 Match_B[Preference_A[i][Now[i]]] = i; 60 } 61 Now[i] ++; 62 } 63 } 64 } 65 inline void Output() 66 { 67 for (int i = 0; i < Num; ++i) 68 { 69 printf("A-%d is matched to B-%d\n", i, Match_A[i]); 70 } 71 } 72 int main() 73 { 74 init(); 75 Gale_Shapley(); 76 Output(); 77 return 0; 78 }
測試一下最初給的那組數據

應用
雖然算法解決的問題是“穩定婚姻匹配”,但是真的按照這個算法來選對象豈不太簡單粗暴了…不過這好歹還是對象包分配
GS算法可以用於解決工作崗位的分配問題,例如某公司有n個崗位,m(m>=n)人競爭,可以根據公司和應聘者對彼此的好感度排序,利用算法算出最合適的人選。
漫談
如果把上文中的人視為點,好感關系視為線,就構成了一個二分圖匹配問題。問題就可以轉化為:根據已有的好感度排序,如何連線才能使得左右兩側的點一一相連,且連線結果盡量令人滿意。
匹配成功后的結果如圖所示,是一個一一映射:

如果把這個問題削弱一下,條件可以改為:每個人都對M(M<=N)名異性有好感,這種好感不分大小。如何使盡量多對互有好感的人相匹配。這個問題稱作二分圖最大匹配問題,可以用匈牙利算法求解。
