KM最容易看懂的教程和参考程序

来源:互联网 发布:金融大数据分析是什么 编辑:程序博客网 时间:2024/06/15 01:48

现在有N男N女,有些男生和女生之间互相有好感,我们将其好感程度定义为好感度,我们希望把他们两两配对,并且最后希望好感度和最大。

怎么选择最优的配对方法呢?

首先,每个女生会有一个期望值,就是与她有好感度的男生中最大的好感度。男生呢,期望值为0,就是……只要有一个妹子就可以啦,不挑~~

这样,我们把每个人的期望值标出来。

接下来,开始配对。

配对方法:

我们从第一个女生开始,分别为每一个女生找对象。

每次都从第一个男生开始,选择一个男生,使女两人的期望和要等于两人之间的好感度

注意:每一轮匹配每个男生只会被尝试匹配一次!

 

具体匹配过程:

==============为女1找对象===============

(此时无人配对成功)

根据 “男女两人的期望和要等于两人之间的好感度”的规则

女1-男1:4+0 != 3

女1-男3:4+0 == 4

所以女1选择了男3

女1找对象成功

==============为女1找对象成功============

 

==============为女2找对象===============

(此时女1—男3)

根据配对原则,女2选择男3

男3有主女1女1尝试换人

我们尝试让女1去找别人

尝试失败

为女2找对象失败!

==============为女2找对象失败============

 

这一轮参与匹配的人有:女1,女2,男3。

怎么办???很容易想到的,这两个女生只能降低一下期望值了,降低多少呢?

任意一个参与匹配女生能换到任意一个这轮没有被选择过的男生所需要降低的最小值

比如:女1选择男1,期望值要降低1。 女2选择男1,期望值要降低1。 女2选择男2,期望值要降低2。

于是,只要期望值降低1,就有妹子可能选择其他人。所以妹子们的期望值要降低1点。

同时,刚才被抢的男生此时非常得意,因为有妹子来抢他,与是他的期望值提高了1点(就是同妹子们降低的期望值相同)。

与是期望值变成这样(当然,不参与刚才匹配过程的人期望值不变)

==============继续为女2找对象=============

(此时女1—男3)

女2选择了男1

男1还没有被配对

女2找对象成功!

==============为女2找对象成功=============

 

==============为女3找对象===============

(此时女1—男3,女2-男1)

女3没有可以配对的男生……

女3找对象失败

==============为女3找对象失败============

此轮只有女3参与匹配

此时应该为女3降低期望值

降低期望值1的时候,女3-男3可以配对,所以女3降低期望值1

==============继续为女3找对象============

(此时女1—男3, 女2-男1)

女3相中了男3

此时男3已经有主女1,于是女1尝试换人

女1选择男1

而男1也已经有主女2,女2尝试换人

前面说过,每一轮匹配每个男生只被匹配一次

所以女2换人失败

女3找对象再次失败

==============为女3找对象失败============

这一轮匹配相关人员:女1,女2,女3,男1,男3

此时,只要女2降低1点期望值,就能换到男2

(前面提过 只要任意一个女生能换到任意一个没有被选择过的男生所需要降低的最小值)

我们把相应人员期望值改变一下

==============还是为女3找对象============

(此时女1—男3, 女2-男1)

女3选择了男3

男3有主女1,女1尝试换人

女1换到了男1

男1已经有主女2,女2尝试换人

女2换人男2

男2无主,匹配成功!!!

==============为女3找对象成功=============

匹配成功!!!撒花~~

到此匹配全部结束

此时

女1-男1,女2-男2,女3-男3

好感度和为最大:9

 

虽然不停换人的过程听起来很麻烦,但其实整个是个递归的过程,实现起来比较简单。比较复杂的部分就是期望值的改变,但是可以在递归匹配的过程中顺带求出来。

#include <iostream>#include <ctime>#include <algorithm>#include <cmath>#include <cstdio>#include <cstring>#include <cstdlib>#include <queue>#include <iomanip>#include <stack>using namespace std;int w[1005][1005];const int inf=(1<<20)-1;int m,n;//n左m右 int cx[1005],cy[1005];//顶标 bool usex[1005],usey[1005];//本回合使用的x,y int link[1005];//link[i]=x代表:在y图中的i与x相连 bool dfs(int u){    usex[u]=1;    for(int i=1;i<=m;i++)        if(!usey[i]&&cx[u]+cy[i]==w[u][i]){            usey[i]=1;            if(link[i]==-1||dfs(link[i])){//注意这里要搜link[i]而不是i,因为我们只搜索x侧的,不需要搜索y侧的                 link[i]=u;                return 1;               }        }return 0;}int KM(){    memset(cy,0,sizeof(cy));    memset(cx,-1,sizeof(cx));    for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)            cx[i]=max(cx[i],w[i][j]);    for(int i=1;i<=n;i++){              while(1){            int d=inf;            memset(usex,0,sizeof(usex));            memset(usey,0,sizeof(usey));            if(dfs(i))break;            for(int i=1;i<=n;i++)                if(usex[i])                    for(int j=1;j<=m;j++)                        if(!usey[j])d=min(d,cx[i]+cy[j]-w[i][j]);            if(d==inf)return -1;            for(int i=1;i<=n;i++)                if(usex[i])cx[i]-=d;            for(int i=1;i<=m;i++)                if(usey[i])cy[i]+=d;        }    }    int ans=0;    for(int i=1;i<=m;i++){        if(~link[i])ans+=w[link[i]][i];    }    return ans; }int main (){    while(~scanf("%d%d",&n,&m)){        memset(w,0,sizeof(w));        memset(link,-1,sizeof(link));        int a,b,c;        for(int i=1;i<=n;i++)            for(int j=1;j<=m;j++)                scanf("%d",&w[i][j]);                       printf("%d\n",KM());    }       return 0;}


原创粉丝点击