http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1576
稳定婚姻是组合数学里面的一个问题。
问题大概是这样:有一个社团里有n个女生和n个男生,每位女生按照她的偏爱程度将男生排序,同时每位男生也按照自己的偏爱程度将女生排序。然后将这n个女生和n个男生配成完备婚姻。如果存在两位女生A和B,两位男生a和b,使得A和a结婚,B和b结婚,但是A更偏爱b而不是a,b更偏爱A而不是B,则这个婚姻就是不稳定的,A和b可能背着别人相伴而走,因为他俩都认为,与当前配偶比起来他们更偏爱各自的新伴侣。如果完备婚姻不是不稳定的,则称其是稳定的。通过证明,可以得到每一个n女n男的社团,都存在稳定婚姻的结论。但是这种情况只在异性的社团中存在。也就是说在同性的社团里面,稳定婚姻的存在性将不再被保证。
实现该问题的算法是:Gale-Shapley 算法。 其算法的大致思路是:第一轮先让所有的男生选择自己最心仪的女生进行表白,当然这时候有些女生会没有男生没有表白,没事接下去会有的。然后女生在所有向自己表白的男生中选出最心仪的男生作为男朋友。 接下去就是第二轮,该轮还是一样,没有女朋友的男生选择没有拒绝过自己的最心仪的女生进行再一次的表白,不管那个女生有没有男朋友,表白完了之后,又是女生选最心仪的男朋友,对于某个女生,要是她发现现男友没有本轮向自己表白的男生好的话, 就可以选择换点男朋友,如此循环下去就可以将所有的男女生进行配对了。
算法的伪代码:while 存在男人m是自由的且还没对每个女人都求过婚
选择这个男人m
令w是m的优先表中还没求过婚的最高排名的女人
if w是自由的
(m,w)变成约会状态
else w当前与m1约会
if w更偏爱m1而不爱m
m保持自由
else w更偏爱m而不爱m1
(m,w)变成约会状态
m1变成自由
endif
endif
endwhile
代码实现:
#include<stdio.h>
#include<string.h>
#include<map>
#include<iostream>
using namespace std;
int N ;
map<string,int> mp_gg ;
map<string,int> mp_mm ;
string ss ,name[510*2];
int maze_gg[510][510] ;
int maze_mm[510][510] ;
int match_gg[510] , match_mm[510] ;
int p[510] ;
void solve(){
memset(match_gg , -1 ,sizeof(match_gg)); //标记男生的对象标号
memset(match_mm , -1 ,sizeof(match_mm)); //标记女生的对象编号
for(int i=1;i<=N;i++){
p[i] = 1 ; //标记男生表白次数
}
bool flag = true ;
while(flag){
flag = false ;
for(int i=1;i<=N;i++){
if(match_gg[i]==-1 && p[i]<=N){ //男生还单身
int a = maze_gg[i][p[i]++] ;
if(match_mm[a]==-1){ //女生也单身
match_mm[a] = i ;
match_gg[i] = a ;
}
else{
if(maze_mm[a][i] < maze_mm[a][match_mm[a]]){ //本轮的男生更优秀 , 替换
match_gg[i] = a ;
match_gg[ match_mm[a] ] = -1 ;
match_mm[a] = i ;
}
}
flag = true ;
}
}
}
for(int i=1;i<=N;i++){
cout << name[i] << " " << name[ N + match_gg[i] ] << endl ;
}
cout << endl ;
}
int main(){
while(cin >> N){
mp_gg.clear() ;
mp_mm.clear() ;
int cnt = 0 ;
for(int i=1;i<=N;i++){
cin >> ss ;
mp_gg[ss] = i ; //男孩的编号
name[i] = ss ; //男孩的名字
for(int j=1;j<=N;j++){
cin >> ss ;
int num ;
if(mp_mm[ss] == 0){
mp_mm[ss] = ++cnt ; //女孩的编号
name[N+cnt] = ss ; //女孩的名字
num = cnt ;
}
else{
num = mp_mm[ss] ;
}
maze_gg[i][j] = num ; //i号男孩第j个心仪女孩的编号
}
}
for(int i=1;i<=N;i++){
cin >> ss ;
int num_mm = mp_mm[ss] ;
for(int j=1;j<=N;j++){
cin >> ss ;
int num_gg = mp_gg[ss] ;
maze_mm[num_mm][num_gg] = j ;
}
}
solve();
}
return 0;
}