Stable Marriage Question

刚刚上完卜东波老师的算法课,听完Stable Marriage Question问题,现把这个算法实现一下:


问题描述:

今天刚研究的一个ACM/ICPC题目,叫做“稳定婚姻问题(The Stable Marriage Problem)”大致说的就是100个SSGG和100个PPMM按照自己的喜欢程度给所有异性打分排序。每个帅哥都凭自己好恶给每个MM打分:我最爱a,其次爱b,再次爱c...每个帅哥打的分不同,你最爱的可能是我最讨厌的我最爱的可能是他不甚喜欢的。同样,每个美女也同样给每个帅哥打分。现在需要给他们搭配出100对新郎新娘,并且要保证所得到是稳定婚姻的搭配。那么,什么是不稳定的婚姻呢?所谓不稳婚姻是说, 比如说有两对夫妇(M1、F1)和(M2、F2),M1的老婆是F1,但他更爱F2;而F2的老公虽说是M2,但她更爱M1——这样的婚姻就是不稳婚姻,M1和F2理应结合,他们现在各自的婚姻都是错误。

那么,我们如何找到一个算法来构造这100个稳定婚姻呢?这个是数学界切切实实研究过的问题。对于以前没有接触过这个问题的人,这个理论最出人意外的结论是: 传统的求爱、结婚过程是male-optimal(男生主动)的,也就是说,男性能够得到尽可能好的心上人,女性却不然。这个问题和图论有关, 最早是由两个美国数学家1962年在American Mathematical Monthly上提出的,相关的参考文献其实很多,下面这个网页大概是讲得最通俗易懂的: "The Stable Marriage Problem" by Harry Mairson,http://www.cs.columbia.edu/~evs/intro/stable/writeup.html

那么,开始激动人心求婚过程啦

第一天上午, 所有的男生都向自己最爱的美眉求婚。下午,每个MM看看自己有没有收到, 收到了多少人的求婚。如果只收到一个男生的求婚,那么就和他订婚。如果收到多于一个GG的求婚,那么就和其中她最爱的那个男人订婚,同时把其他男人都拒掉。如果一个求婚都没有,不要着急,最后总会有的。晚上,检查一遍,如果所有MM都订婚了,OK,万事大吉,明天举行集体婚礼!

但如果还有人没有订婚,那么事情还没有完,第二天还得重复。

第二天上午,所有还没订婚的男生向自己次爱的美眉求婚(因为昨天已经被他们的最爱拒绝了)下午,每个MM再看一遍自己收到订婚的情况。如果她已经订婚了,但是又有一个她更爱的男人来向她求婚,那就把原来那个拒绝掉,再和这个更爱的男人订婚;如果还没订婚,那就和第一天的下午的处理一样。晚上再检查一遍,如果还是有人没有订婚,那第三天再重复。

第三天上午,所有没有订婚的GG,包括第一天订了第二天又被踹出来的(看来要有点忧患意识),再向还没有拒绝过他的MM中他最爱的那个求婚

......
如此周而复始,直到最后大家都订了婚,便一起结婚!哈哈,恭喜恭喜

这么个过程,数学上可以证明如下性质
1) 这个过程会中止,也就是说,总有大家都订了婚的一天,不可能无限循环。
2) 中止后所有的婚姻是稳定婚姻。我们能证明的是,通过上面那个求婚过程,所有的婚姻都是稳定的,没有人犯错误。
3) 比较引人注目的是,这个过程是male-optimal(男生主动)的,男性能够获得尽可能好的伴侣,比如说最后有二十个女人拒绝了他,他仍然能够得到剩下的八十个女人中他最爱的那一个。
4) 更有甚者,这个过程是female-pessimal的,女人总是在可能的情况下被最不喜欢的人追上:eek:。这一点没有那么直观的理解,勉强要解释的话,可以这么看:虽说女人每换一次订婚对象,都往上升一层,但起点可能很低,虽说在一步步接近她最爱的目标,但最后往往达不到。比如说还差三十名就达到她最爱的人了,但这时Game Over,所有的人都已订了婚,这样她也只能死了心了!还有三十个她更爱的人还没向她求过婚,可是她也无可奈何了...

—————————
由此,我们用严格的数学证明得到的结论,也是给MM们的最大教训是,想要好GG,自己就得主动哟~~~哈哈


算法如下:

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>


#define MAX 1000


bool AllManHave( int * tagMan, int n )  //是不是所有的男人都有了结婚对象了
{
for (int i = 0; i<n; i++)
{
if (tagMan[i] != MAX)
continue;
else return false;
}
return true;
}


int SelectManNotHave( int * tagMan, int n)  //随机选取一个没有结婚的男人,先选取第一个没有结婚对象的男人
{
for(int i = 0; i < n; i++)
{
if (tagMan[i] == MAX)
{
return i;
}
}
return MAX; //其实是不会发生的
}


int IndexInWomenSelect( int  manOrder , int * womenSelect , int k, int n )//由男人的顺序号来推知该男人在第k位女人的心中排第几位
{
for (int i = 0; i<n; i++)
{
if (manOrder == womenSelect[k*n + i])
{
return i;
}
}
return MAX;
}


void FindStableMarriage()
{
puts("please input the counts of man and woman:"); //输入有多少对男女
int n = 0;
scanf("%d",&n);
int *menSelect = (int *)malloc(n*n*sizeof(int));//男人们的选择存储到这个数组中
int *womenSelect = (int *)malloc(n*n*sizeof(int));//女人们的选择存储到这个数组中
int *tagMan = (int *)malloc(n*sizeof(int));//用于标示男人有没有女人,有的话为女人的编号,没有为MAX
int *tagWoman = (int *)malloc(n*sizeof(int));//用于标示女人有没有男人,有的话为男人的编号,没有为MAX
//初始化
int i = 0,j=0;
for (i = 0;i<n;i++)
{
tagMan[i] = MAX;
tagWoman[i] = MAX;
}
for (i=0;i<n;i++)
{
printf("please input the order of the %dth man's lover:",i);
for(j=0;j<n;j++)
{
scanf("%d", menSelect+i*n+j);
}
}
for (i=0;i<n;i++)
{
printf("please input the order of the %dth woman's lover:",i+1);
for(j=0;j<n;j++)
{
scanf("%d", womenSelect+i*n+j);
}
}
while (true)
{
if (AllManHave(tagMan, n))
{
break;
}
int k = SelectManNotHave(tagMan, n);
for (int l = menSelect[k*n],t = 0; t<n; t++,l = menSelect[k*n+t])
{
if (tagWoman[l] == MAX) //说明女人还没有出嫁
{
tagWoman[l] = k;
tagMan[k] = l;
break; //这个男人求婚成功了
}
else if (IndexInWomenSelect(tagWoman[l], womenSelect, l, n) > IndexInWomenSelect(k, womenSelect, l, n)) //说明现在这个求婚的比现在的老公优秀
{
tagMan[tagWoman[l]] = MAX;   //这个悲催的男人就被撬墙角了
tagWoman[l] = k;
tagMan[k] = l;
break ; //这个男人求婚成功了,不过待会有可能会被撬的
}
else ;//说明这个求婚的男人还没有女人现在的老公优秀,还想癞蛤蟆想吃天鹅肉,你就被拒绝了,继续向下一位女士求婚吧
}
}
for (i = 0; i<n; i++)
{
printf("the %dth man marry the %dth woman!\n",i, tagMan[i]);
}
puts("笨鸡蛋和笨鸭蛋结为夫妇!\n"); //嘿嘿,搞笑的~~~~
}


int main()
{
FindStableMarriage();
return 0;
}

运行结果如下:


问题:

1、二维数组与指针的问题。这里我就用一维数组来表示二维数组,这样占用的空间也不大,只是使用的使用有点不太方便,不过在解决此问题还是绰绰有余的~~

2、程序写下来基本上没有什么问题,只是有点小细节没有处理好,导致开始的时候有点小问题。不过调试一下就马上看出来问题了。

3、有时思路不清晰的时候写在纸上能有效提高效率。以后可以好好试一下~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章