首页 > ACM题库 > HDU-杭电 > hdu 1914 The Stable Marriage Problem-组合数学[解题报告]c++
2013
12-27

hdu 1914 The Stable Marriage Problem-组合数学[解题报告]c++

The Stable Marriage Problem

问题描述 :

The stable marriage problem consists of matching members of two different sets according to the member’s preferences for the other set’s members. The input for our problem consists of:a set M of n males;
a set F of n females;for each male and female we have a list of all the members of the opposite gender in order of preference (from the most preferable to the least).
A marriage is a one-to-one mapping between males and females. A marriage is called stable, if there is no pair (m, f) such that f ∈ F prefers m ∈ M to her current partner and m prefers f over his current partner. The stable marriage A is called male-optimal if there is no other stable marriage B, where any male matches a female he prefers more than the one assigned in A.Given preferable lists of males and females, you must find the male-optimal stable marriage.

输入:

The first line gives you the number of tests. The first line of each test case contains integer n (0 < n < 27). Next line describes n male and n female names. Male name is a lowercase letter, female name is an upper-case letter. Then go n lines, that describe preferable lists for males. Next n lines describe preferable lists for females.

输出:

The first line gives you the number of tests. The first line of each test case contains integer n (0 < n < 27). Next line describes n male and n female names. Male name is a lowercase letter, female name is an upper-case letter. Then go n lines, that describe preferable lists for males. Next n lines describe preferable lists for females.

样例输入:

2
3
a b c A B C
a:BAC
b:BAC
c:ACB
A:acb
B:bac
C:cab
3
a b c A B C
a:ABC
b:ABC
c:BCA
A:bac
B:acb
C:abc

样例输出:

a A
b B
c C

a B
b A
c C

题意:稳定婚姻问题的阐述。。

话说在1962年,两个数学家David Gale 和Lloyd Shapley提出了下面的问题:

给定若干个男生和同样多的女生,他们每个人都对所有的异性有一个心理的偏好次序。是否存在一种男女配对组合构成一种稳定的组合关系?这里稳定组合的意思是说,不存在两个非伴侣的异性对彼此的评价比对各自伴侣的评价还要高。(可以理解,这样的异性太容易红杏出墙了,所以是某种不稳定因素。)进一步的问题是,在已知每个人对异性的偏好顺序的情况下,怎样求出这种稳定组合方式(如果它存在的话)?你可以理解为这是数学家们替月老问的问题:给定一群孤男寡女,寻找一种牵红线的方式,以确保把红杏扼杀在摇篮里。

这一问题被称为稳定婚姻问题。它有很多种可能的解法。为了让大家相信数学家不是真得如此无聊,我要指出它确确实实是一个地道的组合数学问题,有其特定的数学价值。当然啦,它也有很多别的背景和应用,比如用来在若干个公司和应聘者之间进行招聘中介……但是数学家们怎么会放过如此八卦的一个名字呢?于是它就这样流传下来了。

给定每个人关于异性的偏好排序,要寻找一种男女配对组合构成稳定的组合。Gale和Shapley不但提出了这个问题本身,而且给出了一种著名的解法。这个解法可以描述为如下的求偶过程:

首先,让这些男生去向他们最心仪的女生求婚——这是数学家们的原本的用词。如果你觉得太快了的话,让我们暂时改成表白吧……

然后,等所有男生表白完毕后,所有的收到表白女生们都从自己的表白者中选择自己最喜欢的人接受为男朋友。没人表白的女生只能暂时等一等了,不要着急,表白会有的。

以上过程称为“一轮”。之后的每一轮都按照类似的方式进行。首先由还处于单身状态的男生们每个人再次向自己还没有表白过的女生中自己最喜欢的人表白(无论人家是否已经有了男朋友),然后,等所有单身男生表白完毕后,所有的收到表白女生们都从自己的表白者中选择自己最喜欢的人接受为男朋友。如果原来有男朋友而表白者中有自己更喜欢的,不要犹豫,换之。等到尘埃落定之后,再开始如上所述的新的一轮表白。

依此类推。可以证明的是,这个过程一定是会终止的,也就是说,不会陷入任何死循环。并且一旦终止,每个人都会找到一个伴侣。更关键的是,这个过程最终得到的一定是如前所述的“稳定组合”:不存在两个非伴侣的异性对彼此的评价比对各自伴侣的评价还要高。——这几个事实都不难证明,有兴趣的话可以自己试试看。

所以这就得到了稳定婚姻问题的一个解(顺便也证明了解的存在性)。但是真正有趣的部分还在后面。一般来说,给定若干个男生女生和他们之间的偏好关系,稳定组合存在不止一种。上述“算法”只是给出了所有可能的稳定组合其中之一而已。但是这个特定的解具有某些特别的性质:可以证明(这一次证明不很容易了),上述方式得到的稳定组合和所有其他的可能的稳定组合相比,是对男生最优而对女生最劣的。

确切地说是这样:

它是对男生最优的。也就是说,对每个男生来说,按照这种方式最后找到的伴侣,是在所有的稳定组合中自己可能具有的伴侣中自己评价最高的。——注意这并不等于说被个男生都能追到自己最喜欢的女生,而只是说,他一定能追到“有可能和他在稳定组合中在一起的女生”中自己最喜欢的。有些女生虽然很好,但是和他在一起是不可能形成稳定组合的。这就是人生啊……

另一方面,它是对女生最劣的。也就是说,对每个女生来说,按照这种方式最后找到的伴侣是在所有的稳定组合中自己可能具有的伴侣中自己评价最低的。同样的,这也不等于说每个女生都只有和自己最不喜欢的男生在一起,而只是说她最后的男朋友会是所有“有可能”的男生中自己觉得最勉强的。不过这样听起来也已经很悲惨了。

这两个结论并不直观,因为看起来在上面所描述的过程中,女生是相对占有优势的。作为男生,需要很辛苦地去不断表白,然后被拒,再表白,再被拒……而女生只要随心所欲挑选就好,而且还有随时更换男友的权利(在上面的规则里男生是不能主动提出分手的)。为什么结局会是如此?

但是如果仔细思考上面所描述的规则,会看到男生至少有一样优势——也许是至关重要的优势:他们是主动方。主动的好处是,即使一次又一次的被拒,他也仍然可以和剩下的女生中自己最喜欢的在一起。而对于女生来说,纵然有再多挑选的自由,可是一个女生也许永远也等不到自己最喜欢的男生来追自己——或者在她等到之前,游戏就已经结束了。

毫无疑问,你已经看出在上面的设定里“男生”和“女生”都只是代号而已,它符合古典文学的一贯叙事,但是在当代语境里也许并不政治正确。另一方面,这个定理也不是真的用来描述爱情的——数学家们还没有这么疯狂,认为可以用逻辑来推理情感。它只是一个过于简化的模型而已,比张生和维特的故事还要不靠谱的多。

但是我也相信你一定已经看出了我这篇文章的主题。在一切古典文学的叙事里,我们都满怀着希望注视着那些勇敢的孩子们,看着他们的努力和坚持,也许最后会失败,可是他们至少尝试过。

现在连数学也在帮着说明这个道理了,你还等什么呢?

**********************************************************************************

这个问题确实是组合数学上的经典问题。具体算法的证明可以参考《组合数学》二分图匹配中的稳定婚姻问题。

这个算确实是在一个偶然的机会下接触到的,但在认真看完书后,确实颇有感想:男生不断地去像女生表白,然后不断地被拒绝···看似十分郁闷,但得到的结果却是在所有可能的结果中对男生最有利的结果。而女生看似十分幸福,但得到的确实是最差的结果。

代码:

#include<iostream>
using namespace std ;
const int N = 40 ;
struct male
{
     int f,rev[N],tag;
}m[N];
struct female
{
     int tag,temp,val,wait[N];   
}f[N];
int n , t , k , mf[N][N] , fm[N][N];    
char ch[N]; 
bool ok()
{
    for(int i=1;i<=26;i++)
        if(m[i].f==0&&m[i].tag>0) return true ;
    return false ;
} 
int main()
{
    int T ;
    scanf("%d",&T) ;
    while(T--)
    { 
    scanf("%d",&n); 
    for(int i=1;i<=26;i++)
        f[i].tag=0,m[i].tag=0;  
    for(int i=1;i<=n;i++)
    {
        scanf("%s",&ch);
        t = ch[0]-'a' + 1 ;
        m[t].f=0 , m[t].tag=t;
        memset(m[t].rev,0,sizeof(m[t].rev));       
    } 
    for(int i=1;i<=n;i++)
    {
        scanf("%s",&ch);
        t = ch[0]-'A' + 1 ;
        f[t].tag = t ;
        f[t].temp = 0 ;
        f[t].val = 30 ;
        memset(f[t].wait,0,sizeof(f[t].wait));      
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%s",&ch);
        t = ch[0] - 'a' + 1 ;
        for(int j=2;j<=n+1;j++)
            mf[t][j-1] = ch[j] -'A' + 1 ;        
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%s",&ch);
        t = ch[0] - 'A' + 1 ;
        for(int j=2;j<=n+1;j++)
            fm[t][j-1] = ch[j] -'a' + 1 ;        
    }
    while(ok())
    {
        for(int i=1;i<=26;i++)
        {
            if(m[i].f==0&&m[i].tag>0)
            {
                  for(int j=1;j<=n;j++)
                  {
                       t = mf[i][j] ;  
                       if(m[i].rev[t]==0)
                       {
                             m[i].rev[t] = 1 ;
                             m[i].f = 1 ;
                             k = ++f[t].wait[0] ;    
                             f[t].wait[k] = i ;   
                             break;
                       }             
                  }    
            }   
        }
        for(int i=1;i<=26;i++)
        {
            if(f[i].tag>0)
            {
                for(int j=1;j<=f[i].wait[0];j++)
                {
                      t = f[i].wait[j] ;
                      for(k=1;k<=n;k++)
                      {
                          if(fm[i][k]==t)  break; 
                      } 
                      if(f[i].val>k)
                      {
                          m[f[i].temp].f = 0 ;
                          f[i].temp = t ;  
                          f[i].val = k ;
                      }
                      else m[t].f = 0 ;  
                }                
                f[i].wait[0] = 0 ;
            } 
        }
    }  
    int out[N]; 
    memset(out,0,sizeof(out));
    for(int i=1;i<=26;i++)
    {
          if(f[i].tag>0)
          {
               int j = f[i].temp ;
               out[j] = i ; 
          }
    }  
    for(int i=1;i<=26;i++)
    {
          if(out[i]) printf("%c %c/n",i-1+'a',out[i]-1+'A');
    }
    if(T) printf("/n"); 
    }
    return 0 ;
}

解题转自:http://blog.csdn.net/yuhailin060/article/details/5405422


  1. Good task for the group. Hold it up for every yeara??s winner. This is a excellent oppotunity for a lot more enhancement. Indeed, obtaining far better and much better is constantly the crucial. Just like my pal suggests on the truth about ab muscles, he just keeps obtaining much better.

  2. 约瑟夫也用说这么长……很成熟的一个问题了,分治的方法解起来o(n)就可以了,有兴趣可以看看具体数学的第一章,关于约瑟夫问题推导出了一系列的结论,很漂亮

  3. #include <stdio.h>
    int main(void)
    {
    int arr[] = {10,20,30,40,50,60};
    int *p=arr;
    printf("%d,%d,",*p++,*++p);
    printf("%d,%d,%d",*p,*p++,*++p);
    return 0;
    }

    为什么是 20,20,50,40,50. 我觉得的应该是 20,20,40,40,50 . 谁能解释下?

  4. 第二个方法挺不错。NewHead代表新的头节点,通过递归找到最后一个节点之后,就把这个节点赋给NewHead,然后一直返回返回,中途这个值是没有变化的,一边返回一边把相应的指针方向颠倒,最后结束时返回新的头节点到主函数。

  5. 可以根据二叉排序树的定义进行严格的排序树创建和后序遍历操作。如果形成的排序树相同,其树的前、中、后序遍历是相同的,但在此处不能使用中序遍历,因为,中序遍历的结果就是排序的结果。经在九度测试,运行时间90ms,比楼主的要快。