HDOJ 1435 Stable Match(稳定匹配)



点击打开链接



(一)问题的引出:

有N男N女,每个人都按照他对异性的喜欢程度排名。现在需要写出一个算法安排这N个男的、N个女的结婚,要求两个人的婚姻应该是稳定的。

何为稳定?

有两对夫妻M1 F2,M2 F1。M1心目中更喜欢F1,但是他和F2结婚了,M2心目中更喜欢F2,但是命运却让他和F1结婚了,显然这样的婚姻是不稳定的,随时都可能发生M1和F1私奔或者M2和F2私奔的情况。所以在做出匹配选择的时候(也就是结婚的时候),我们需要做出稳定的选择,以防这种情况的发生。




(二)算法介绍:

参考:http://www.matrix67.com/blog/archives/2976

1962 年,美国数学家 David Gale 和 Lloyd Shapley 发明了一种寻找稳定婚姻的策略。不管男女各有多少人,不管他们各自的偏好如何,应用这种策略后总能得到一个稳定的婚姻搭配。换句话说,他们证明了稳定的婚姻搭配总是存在的。有趣的是,这种策略反映了现实生活中的很多真实情况。



算法中采用了男生主动追求女孩的形式。

算法步骤描述:


第一轮,每个男人都选择自己名单上排在首位的女人,并向她表白。这种时候会出现两种情况:(1)该女士还没有被男生追求过,则该女士接受该男生的请求。(2)若该女生已经接受过其他男生的追求,那么该女生会将该男士与她的现任男友进行比较,若更喜欢她的男友,那么拒绝这个人的追求,否则,抛弃其男友(囧)……


第一轮结束后,有些男人已经有女朋友了,有些男人仍然是单身。

在第二轮追女行动中,每个单身男都从所有还没拒绝过他的女孩中选出自己最中意的那一个,并向她表白,不管她现在是否是单身。这种时候还是会遇到上面所说的两种情况,还是同样的解决方案。直到所有人都不在是单身。




怎么证明这个算法肯定能够得到稳定的婚姻:

(1)随着轮数的增加,总有一个时候所有人都能配上对。因为男生根据自己心目中的排名依次对女士进行表白,假如有一个人没有配上对,那么这个人必定是向所有的女孩进行表白了。但是女孩只要被表白过一次,就不可能是单身,也就是说此时所有的女生都不是单身的,这与有一个人没有配上对是相悖的。所以假设不成立。该算法一定会使得所有人都能够配对成功。

(2)随着轮数的增加,男士追求的对象越来越糟,而女士的男友则可能变得越来越好。假设男A和女1各有各自的对象,但是比起现在的对象,男A更喜欢女1,所以,在此之前男A肯定已经跟女1表白过的,并且女1拒绝了男A或者甩了男A,也就是女1有了比男A更好的男友,不会出现私奔的情况……。


在这道题目里面,只不过是把人换成了信号的发射站和接收站。并且,人的好感度变成了信号发射站和接收站的距离和容量的综合考量。首先有一个问题,这道题中,把发射站看成男性还是把接收站看成男性,有关系吗?答案是没有关系,因为在设计算法的时候,男追女这样一个模式只是人为定义的,为的是得到最后的结果,一个稳定匹配。事实上如果是女追男也没有什么问题。所以无论是把发射站看成男性还是把接收站看成男性,都可以。这道题中,我把发射站看作是男性。


getdist计算距离时不用开方了,因为反正只要比较大小。注意cmp数组的写法,以及sort是左闭右开。




#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define eps 1e-8
#define MAXN 205
using namespace std;
struct temp{
int pos;
int c;
double dist;
}Rank[MAXN];
struct point{
int v;
double x;
double y;
double z;
}s[MAXN], e[MAXN];
int ranks[MAXN][MAXN], ranke[MAXN][MAXN], man[MAXN], woman[MAXN], tempman[MAXN];
int womanpos, flag, T, a, b, n;
double d, f, g;
double getdist(point A, point B)
{
    return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + (A.z - B.z) * (A.z - B.z);
}
bool cmp(temp a, temp b)
{
    if (fabs(a.dist - b.dist) < eps)
        return a.c > b.c;
        else return a.dist < b.dist;
}
void sortrank()
{
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            Rank[j].pos = j;
            Rank[j].c = e[j].v;
            Rank[j].dist = getdist(s[i], e[j]);
        }
        sort(Rank+1, Rank+n+1, cmp);
        for (int j = 1; j <= n; j++)
            ranks[i][j] = Rank[j].pos;//编号为i的男性第j喜欢的是编号为ranks[i][j]的女性
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            Rank[j].pos = j;
            Rank[j].c = s[j].v;
            Rank[j].dist = getdist(e[i], s[j]);
        }
        sort(Rank+1, Rank+n+1, cmp);
        for (int j = 1; j <= n; j++)
            ranke[i][Rank[j].pos] = j;//对于编号为i的女性编号为j的男性是第ranke[i][j]位喜欢的
    }
}
void GS()
{
    memset(man, 0, sizeof(man));
    memset(woman, 0, sizeof(woman));
    for (int i = 1; i <= n; i++) tempman[i] = 1;//tempman[i]表示编号为i的男性接下来要追求的女性的编号
    flag = 1;
    while (flag)
    {
        flag = 0;
        for (int i = 1; i <= n; i++)
            if (man[i] == 0)
        {
            flag = 1;
            womanpos = ranks[i][tempman[i]];//womanpos表示当前的男性所追求的女性的编号
            if (woman[womanpos] == 0)
            {
                woman[womanpos] = i;//woman[i]表示编号为i的女性当前男友的编号,如果还没有男友,那么值为0
                man[i] = womanpos;//man[i]表示编号为i的男性当前女友的编号,如果还没有女友,那么值为0
                tempman[i]++;
            }
            else
            {
                if (ranke[womanpos][i] < ranke[womanpos][woman[womanpos]])
                {
                    man[woman[womanpos]] = 0;
                    woman[womanpos] = i;
                    man[i] = womanpos;
                    tempman[i]++;
                }
                else
                    tempman[i]++;
            }
        }
    }
}
int main()
{
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d%d%lf%lf%lf", &a, &b, &d, &f, &g);
            s[a].v = b;
            s[a].x = d;
            s[a].y = f;
            s[a].z = g;
        }
        for (int i = 1; i <= n; i++)
        {
            scanf("%d%d%lf%lf%Lf", &a, &b, &d, &f, &g);
            e[a].v = b;
            e[a].x = d;
            e[a].y = f;
            e[a].z = g;
        }
        sortrank();
        GS();
        for (int i = 1; i <= n; i++)
            printf("%d %d\n", woman[i], i);
        printf("\n");
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值