今天上课研究生学长讲了一个问题,男女稳定匹配问题,作为一只单身狗觉得这个问题很有意思,下课就试着自己写了下代码。
现有n个男生与n个女生,一男一女配对,要求:
配对的男生与女生互相都是称心的、中意的,且配偶中任一个人都不能有其他彼此更中意的异性。即若给定(A,B)和(C,D)分别是两对配偶,若A和D彼此的喜欢程度都大于自己的配偶(A对D的喜欢程度胜于对B,并且D对A的喜欢程度也胜于对C,这样就会出现A与D私奔的情况),这样是不存在的,也就是说,除了自己的配偶之外,没有更适合的人了。
选择方式:
男生选择自己喜欢的女生并向其告白(有可能多个男生对一个女),女生从对自己告白的男生里面挑选自己最中意的男生作为配偶,被拒绝的男生对其他(排除拒绝自己的女生)女生按照喜欢度排序,并向最喜欢的那个女生告白,所有女生从对自己告白的男生里面挑出最喜欢的一位作为配偶(包括上一步已经选过男生的女生,女生选择时也要考虑上一个选择的男生的喜欢程度是否高于这一次对自己告白的男生)。循环上面的步骤,直到所有男生与女生都配对成功。
这样说比较绕口,用图解描述一下吧。
假设现在有3个男生(A,B,C)与3个女生(1,2,3)。
喜欢程度排序:
第一次男生选取时,挑选自己最中意的女生,A—2, B—1, C—1, 向女生告白。
女生从这些向自己告白的男生中选取自己喜欢程度较高的男生,1—C, 2—A,1女生有两个男生向其告白(B和C),而1女生更喜欢C,所以选择C男生。
B男生被拒绝,在自己的喜欢程度排序中将1排除。
第二次男生选取,A与C已经被选,只剩下B男生,B选择2(因为1女生已经拒绝了B,所以排除1之后最中意的女生是2)。女生2原来选择的B男生,但是在A和B之间,女生更喜欢B,所以拒绝A,选择B。
男生A伤心地从排序中删除了2。
第三次选取,只剩下A还没有配偶,A按照排序选择3,3没有更多的选择,就与A作为配偶。
这样就完成了选取步骤。
可见该步骤是以男生为主体,按照男生的排序做选择,而女生按照排序从某几个男生中挑选。也就是说,在选择时,男生是主动的,女生是被动的,男生可以按照自己喜欢的程度挑选女生,而女生可能会挑选到排序末位的男生。从上面的结果可以看到,女生3的配偶A在排序中是最后一位。
所以说,这样的选择方式男生比较有益。
说是通过这种方法得到的匹配结果是稳定的,即配偶中不会出现私奔情况。确实也是这样,女生3最喜欢男生C,但是男生C根本不会搭理女生3,因为排在了末尾,同理B。
以下是伪代码
while(男生还有自由的,没有配偶的)
{
//男生boy,中意女生girl;
if(girl没有配偶)
{
W与m配对;
M设为不自由
}
else
{ //女生girl的现任配偶是boy’
if(boy’比boy喜欢程度强)
{
boy的排序中删除女生girl;
}
else
{
boy与girl配对;
boy’设为自由,从排序中删除女生girl;
}
}
}
光说没有用,写了下代码。
给男生和女生分别设立结构体,
struct BOYS
{
int Blike[10]; //喜欢程度排序,女生的下标
int free; //是否已经有配偶,是否是自由的
int point; //排序中作为下标,指示男生目前最中意的人
int soul; //男生配偶的下标
};
struct GIRLS
{
int Glike[10];
int soul; //女生配偶的下标
};
那么伪代码可以进行修改:
for(int i 所有男生)
{
if(男生[i].free==0)
Continue;
//男生中意女生value
if(女生[value].soul==0)
{
男生[i].soul=value;
女生[value].soul=i;
男生[i].free=0;
}
else
{
//女生现任配偶与现在告白男生i之间做比较
//a==1说明现任男友排前,0则告白男生排前
int a = compared(女生[value].soul, i, value);
if(a==1)
{
男生[i].point++; //指向下一个中意女生
}
else if(a==0)
{
//更新前任值
男生[女生[value].soul].free=1;
男生[女生[value].soul].point++;
//男生i与女生value配偶
男生[i].soul=value;
女生[value].soul=i;
男生[i].free=0;
}
}
}
配对的核心代码
void couple()
{
for(int i=1;i<=n;i++)
{
if(boys[i].free==0) continue;
int value = boys[i].Blike[boys[i].point];
if(girls[value].soul==0)
{
boys[i].soul=value;
boys[i].free=0;
girls[value].soul=i;
//printf("%d %d\n",i,boys[i].soul);
}
else
{
int a = Compared(girls[value].soul,i,value); //1,0
if(a==1)
{
boys[i].point++;
}
else if(a==0)
{
//更新前男友值
boys[girls[value].soul].free=1;
boys[girls[value].soul].point++;
//printf("boy soul from %d %d",girls[value].soul,value);
//配对
boys[i].soul=value;
girls[value].soul=i;
boys[i].free=0;
//printf(" to %d %d\n",i,boys[i].soul);
}
}
}
}
Compared函数代码
int Compared(int a,int b,int value) //比较男生a和b哪一个在排序中靠前
{
int i;
for(i=1;i<=n;i++)
{
if(girls[value].Glike[i]==a)
return 1;
if(girls[value].Glike[i]==b)
return 0;
}
return -1;
}
假设现有6个男生6个女生,先输入每个男生的喜欢程度排序,再输入每个女生的。
6
2 6 5 1 3 4
3 6 5 1 2 4
4 5 3 2 1 6
1 2 3 4 6 5
3 2 1 6 5 4
2 3 1 5 6 4
2 1 3 5 6 4
4 3 2 6 5 1
3 4 1 5 6 2
6 5 4 3 2 1
4 6 1 3 5 2
1 3 4 6 2 5
最终配对结果:1—6, 2—5, 3—4, 4—1, 5—3, 6—2 (先男生后女生)