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、有時思路不清晰的時候寫在紙上能有效提高效率。以後可以好好試一下~~~~

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