zoukankan      html  css  js  c++  java
  • 【算法笔记】稳定婚姻匹配问题GS算法

    什么是稳定(婚姻)匹配问题

    这里是百度百科。有N男N女,均为异性恋,每个人都对异性有好感度排序。如何将他们两两配对,才能尽可能使结果令每个人都满意。

    当然也有N男M女、多对一的情况,这里先不讨论(网上有些大牛写了论文)。

    处理方案

    被广泛认可的算法是由美国数学家 David Gale 和 Lloyd Shapley 于1962年发明的 Gale-Shapley算法,简称GS算法。GS算法的思路如下:

    先给N男N女从0~N-1分别编号,然后要求每个人写出他们对异性的好感度排序。为了方便,这里N取4.比如0号男最喜欢3号女,其次是0号女,再次是1号女,最后是2号女,则0号男给出的排序为3 0 1 2.

    下图中展示了好感排序情况(随机给出):

    GS_data

    第一轮,先让每个男性向他们最喜欢的女性表白(也可以女性表白男性,同理)。例如0号男会先向3号女表白。如果一个女性同时收到了两个表白,她会选择接受自己最喜欢的那个男性。如3号女同时收到0号和2号的表白,接受0号。
    第一次匹配结束后的情况:
    0号男-3号女    1号男-0号女    3号男-1号女

    第二轮,由于第一轮时只有2号男落单(惨),这次2号男选择向自己第二喜欢的1号女表白。1号女接到表白后,会比较现任对象(3号男)和2号男,发现自己更喜欢2号男,于是与3号男分手,投入2号男的怀抱。
    第二次匹配结束后的情况:
    0号男-3号女    1号男-0号女    2号男-1号女

    第三轮,3号男向自己第二喜欢的0号女表白,0号女比较现任配偶(1号男)和3号男,觉得自己还是更喜欢现任配偶,3号男被残忍拒绝。这次匹配关系没有发生变化。

    第四轮,3号男向自己第三喜欢的2号女表白。2号女还没有收到过表白,直接接受。
    第四次匹配结束后的情况:
    0号男-3号女    1号男-0号女    2号男-1号女    3号男-2号女

    此时每个人都找到了配偶,匹配结束。

    简单总结一下,每一轮开始时:
    ①没有配偶的男性选择自己好感度最高而且没表白过的女性表白。
    ②接到表白的女性比较现任配偶和新追求者的好感度,选择与好感度更高的男性重新匹配。

    GS算法的局限性

    从算法过程容易看出,按照“男性表白女性”的策略对女性更有利,因为女性的配偶质量会不断提升,而男性的配偶质量会不断变差。相反则对男性有利。不同的表白策略得出的匹配结果很可能是不同的。

    GS算法能否保证每个人都有对象

    假设有一个男性A落单,由于男女人数相等,则必定还有一个女性B落单。
    根据算法,B是被动接受表白,只要收到过一次表白,B就不可能落单。
    而如果A没有配偶,他会一直表白下去,将N个女性全部表白一遍。这个过程中B一定会收到表白,两人匹配。

    上述内容同时可以证明,该算法会在有限次运算后结束(运算次数小于n*n)。

    GS算法的结果是否稳定

    “稳定”是指:算法结束后,如果再进行一轮表白,一定不会出现男女双方的选择都更优的情况。
    这一点容易证明。根据算法局限性,再进行表白,男性对新配偶的好感度只会下降。

    假设男性A与女性B本应匹配(也就是说现在两个人分别有自己的配偶,但是从好感度角度来说,他们都更希望彼此形成新的匹配),可以分两种情况讨论:
    1.男性A曾对女性B表白,但此时两人没有匹配,说明女性B后来遇到了好感度更高的男性,假设不成立。
    2.男性A不曾对女性B表白,说明男性A的配偶好感度排在女性B前面,假设不成立。

    代码实现

     1     #include<cstdio>
     2     #include<algorithm>
     3     #include<cmath>
     4     #include<iostream>
     5     #include<cstring>
     6     #include<string>
     7     using namespace std;
     8     const int MAXN = 0 + 5;
     9     int Num,nowMatch;
    10     int Match_A[MAXN], Match_B[MAXN], Preference_A[MAXN][MAXN], Preference_B[MAXN][MAXN], Now[MAXN];
    11     //Match数组记录匹配情况,Preference[i][1..n]记录好感排序
    12     inline void init()
    13     {//读入、预处理
    14       scanf("%d", &Num);
    15       for (int i = 0; i < Num; ++i)
    16         for (int j = 0; j < Num; ++j)
    17           scanf("%d", &Preference_A[i][j]);
    18       for (int i = 0; i < Num; ++i)
    19         for (int j = 0; j < Num; ++j)
    20           scanf("%d", &Preference_B[i][j]);
    21       for (int i = 0; i < Num; ++i)
    22         Match_A[i] = -1, Match_B[i] = -1;
    23     }
    24     bool Compare(int a, int b, int Obj)
    25     {//比较追求当前的B[i]的两个人在B[i]处的好感排序
    26       int index_a = -1, index_b = -1;
    27       for (int i = 0; i < Num; ++i)
    28       {
    29         if (Preference_B[Obj][i] == a)
    30           index_a = i;
    31         else if (Preference_B[Obj][i] == b)
    32           index_b = i;
    33       }
    34       if (index_a < index_b) return true;
    35       return false;
    36     }
    37     bool AllMatch()
    38     {//判断是否完成匹配
    39       int count = 0;
    40       for (int i = 0; i < Num; ++i)
    41         if (Match_A[i] != -1)
    42           count++;
    43       if (count < Num) return false;
    44       return true;
    45     }
    46     void Gale_Shapley()
    47     {
    48       while (! AllMatch())
    49       {//算法会在所有人完成匹配后终止
    50         for (int i = 0; i < Num; i ++)
    51         {
    52           if (Match_A[i] != -1) continue;
    53           nowMatch = Match_B[Preference_A[i][Now[i]]];
    54           //nowMatch是A[i]想要表白的B[i]目前的对象,没有则为-1
    55           if (nowMatch == -1 || Compare(i, nowMatch, Preference_A[i][Now[i]]))
    56           {//如果B[i]没有对象,或者B[i]对A[i]更有好感,则A[i]和B[i]匹配
    57             Match_A[i] = Preference_A[i][Now[i]];
    58             Match_A[nowMatch] = -1;
    59             Match_B[Preference_A[i][Now[i]]] = i;
    60           }
    61           Now[i] ++;
    62         }
    63       }
    64     }
    65     inline void Output()
    66     {
    67       for (int i = 0; i < Num; ++i)
    68       {
    69         printf("A-%d is matched to B-%d\n", i, Match_A[i]);
    70       }
    71     }
    72     int main()
    73     {
    74       init();
    75       Gale_Shapley();
    76       Output();
    77       return 0;
    78     }
    GS code

    测试一下最初给的那组数据

    example

    应用

    虽然算法解决的问题是“稳定婚姻匹配”,但是真的按照这个算法来选对象岂不太简单粗暴了…不过这好歹还是对象包分配
    GS算法可以用于解决工作岗位的分配问题,例如某公司有n个岗位,m(m>=n)人竞争,可以根据公司和应聘者对彼此的好感度排序,利用算法算出最合适的人选。

    漫谈

    如果把上文中的人视为点,好感关系视为线,就构成了一个二分图匹配问题。问题就可以转化为:根据已有的好感度排序,如何连线才能使得左右两侧的点一一相连,且连线结果尽量令人满意。

    匹配成功后的结果如图所示,是一个一一映射:

    如果把这个问题削弱一下,条件可以改为:每个人都对M(M<=N)名异性有好感,这种好感不分大小。如何使尽量多对互有好感的人相匹配。这个问题称作二分图最大匹配问题,可以用匈牙利算法求解。

  • 相关阅读:
    golang 带参数 发送、上传本地文件到其他机器、服务器
    【比赛游记】北大集训2020垫底记
    【比赛游记】NOIP2020又当工具人记
    AtCoder Regular Contest 107
    AtCoder Regular Contest 108
    【比赛游记】CSP2020游记
    注意事项
    2020北大集训摸鱼记
    NOIP2020游记
    ARC109F
  • 原文地址:https://www.cnblogs.com/kamigen/p/13468037.html
Copyright © 2011-2022 走看看