稳定婚姻匹配

摘自:https://www.cnblogs.com/AndyJee/p/4986741.html

题目:

婚介所登记了N位男孩和N位女孩,每个男孩都对N个女孩的喜欢程度做了排序,每个女孩都对N个男孩的喜欢程度做了排序,你作为月老,能否给出稳定的牵手方案?

稳定的定义:如果男孩i和女孩a牵手,但男孩i对女孩b更喜欢,而女孩b的男朋友j拼不过男孩i,则没有力量阻碍男孩i和女孩b的私奔,这即是不稳定的。

思路:

1962 年,美国数学家 David Gale 和 Lloyd Shapley 发明了一种寻找稳定婚姻的策略。不管男女各有多少人,不管他们各自的偏好如何,应用这种策略后总能得到一个稳定的婚姻搭配。换句话说,他们证明了稳定的婚姻搭配总是存在的。有趣的是,这种策略反映了现实生活中的很多真实情况。

算法中采用了男生主动追求女孩的形式。

算法步骤描述:

第一轮,每个男人都选择自己名单上排在首位的女人,并向她表白。这种时候会出现两种情况:

(1)该女士还没有被男生追求过,则该女士接受该男生的请求。

(2)若该女生已经接受过其他男生的追求,那么该女生会将该男士与她的现任男友进行比较,若更喜欢她的男友,那么拒绝这个人的追求,否则,抛弃其男友

第一轮结束后,有些男人已经有女朋友了,有些男人仍然是单身。

在第二轮追女行动中,每个单身男都从所有还没拒绝过他的女孩中选出自己最中意的那一个,并向她表白,不管她现在是否是单身。这种时候还是会遇到上面所说的两种情况,还是同样的解决方案。直到所有人都不再是单身。

怎么证明这个算法肯定能够得到稳定的婚姻?

(1)随着轮数的增加,总有一个时候所有人都能配上对。因为男生根据自己心目中的排名依次对女士进行表白,假如有一个人没有配上对,那么这个人必定是向所有的女孩进行表白了。但是女孩只要被表白过一次,就不可能是单身,也就是说此时所有的女生都不是单身的,这与有一个人没有配上对是相悖的。所以假设不成立。该算法一定会使得所有人都能够配对成功。

(2)随着轮数的增加,男士追求的对象越来越糟,而女士的男友则可能变得越来越好。假设男A和女1各有各自的对象,但是比起现在的对象,男A更喜欢女1,所以,在此之前男A肯定已经跟女1表白过的,并且女1拒绝了男A,也就是女1有了比男A更好的男友,不会出现私奔的情况……。

#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 10;
using namespace std;

int n; 
int w_love[maxn][maxn], m_love[maxn][maxn];		// 女孩,男孩的喜爱排名 
int boyfriend[maxn], girlfriend[maxn];
int man_rejected[maxn];					// 男孩i被拒绝的次数 
int rank[maxn][maxn];					// rank[i][j]: 男孩 j 在女孩 i 心中的的排名 
					
void GaleShapley(){
	for(int i = 0; i < n; ++i){
		boyfriend[i] = -1; girlfriend[i] = -1;
		man_rejected[i] = 0;
		for(int j = 0; j < n; ++j) rank[i][w_love[i][j]] = j; 
	}
	bool single = true;
	while(single){
		single = false;
		for(int i = 0; i < n; ++i){
			if(girlfriend[i] != -1) continue;		// 男孩已经有女友
			single = true;
			int j = man_rejected[i]++;
			int w = m_love[i][j];		// 男孩可追求的最喜欢的女孩
			int m = boyfriend[w];		// 女孩当前男友
			if(m == -1||rank[w][i] < rank[w][m]){	// 相比现男友,女孩更喜欢男孩 i 
				boyfriend[w] = i; girlfriend[i] = w;
				if(m != -1) girlfriend[m] = -1;		// 现男友被抛弃,变成单身 
			}
		}
	}
}
void Print(){
    for(int i = 0; i < n; ++i) printf("Boy %d matches Girl %d\n", i+1, girlfriend[i]+1);
    printf("\n");
}


int main()
{
    freopen("in.txt","r",stdin);
    while(scanf("%d",&n) == 1){
    	for(int i = 0; i < n; ++i)
    	for(int j = 0; j < n; ++j) scanf("%d",&m_love[i][j]);
    	for(int i = 0; i < n; ++i)
    	for(int j = 0; j < n; ++j) scanf("%d",&w_love[i][j]);
    	GaleShapley();
		Print();
	}
	
    fclose(stdin);
	return 0;
}

附几组测试数据:

3
0 1 2
2 1 0
2 0 1
1 2 0
0 1 2
1 0 2
4
0 1 2 3
0 3 2 1
1 0 2 3
3 1 2 0
3 2 0 1
1 3 0 2
3 0 2 1
2 1 0 3
4
2 3 1 0
2 1 3 0
0 2 3 1
1 3 2 0
0 3 2 1
0 1 2 3
0 2 3 1
1 0 3 2
5
2 1 4 5 3
4 2 1 3 5
2 5 3 4 1
1 4 3 2 5
2 4 1 5 3
5 1 2 4 3
3 2 4 1 5
2 3 4 5 1
1 5 4 3 2
4 2 5 3 1

输出:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值