文章目录
KM算法
参考题解,可以跳题解看我只是在尝试理解
求完美匹配的最大值。其实可以理解为一个相亲问题,有n男n女,女生i对每个男生j有一个好感度
l
o
v
e
[
i
]
[
j
]
love[i][j]
love[i][j],而男生对女生的好感度不重要,全部设置为0。
每个女生i都对自己的爱情有一个期望值 e x _ g i r l [ i ] ex\_girl[i] ex_girl[i],最理想的情况每个女生都希望自己可以和最有好感度的男生在一起,因此她们对爱情的初始期待值 e x _ g i r l [ i ] = m a x ( l o v e [ i ] [ j ] ) ex\_girl[i]=max(love[i][j]) ex_girl[i]=max(love[i][j]),在匹配的时候如果一个男生达到女生的期望值就可以和她匹配,如果达不到就免谈。
但这样可以会导致有的男生没有女生要,因此KM算法成为一个月老,它的作用是说服一些女生降低期望值,以使得所有的男生都有女生要,并且最终所有女生对所配的男生的好感度的总和最大。
KM算法按顺序为每一个女生处理她们的归属问题,每个女生又可以匹配很多轮,并且同时减低对爱情的期望值,直到她自己可以匹配成功。
每一轮匹配一个男生只能被尝试匹配一次,不管那次是成功还是失败,如果某个男生可以匹配到女生就皆大欢喜,否则他知道他还差多少才能达到女生的好感度,这个差距是 g a p gap gap,因为在多轮匹配中男生会和很多个女生配对又失配,因此他会聪明地将 g a p gap gap取最小,这个最小值就是 s l a c k slack slack。
每个女生匹配一轮失败,就会降低期望值,减低到什么程度才会使得下一轮有所进展呢?就是有某个在这一轮中失配的男生,在下一轮至少可以获得一个女生的芳心,因此女生的期望值应该降低到这个男生够得到,因此减去 d = m i n ( s l a c k [ i ] ) d=min(slack[i]) d=min(slack[i]), i i i表示所有失配男生。
但是降低期望值是所有这一轮匹配过的女生一起降低
d
d
d,且匹配过的男生也增加了
d
d
d的期望值,同时失配男生的
s
l
a
c
k
slack
slack都会减去最小的男生的
s
l
a
c
k
slack
slack值
d
d
d。
模板题 P1559 运动员最佳匹配问题
#include<bits/stdc++.h>
using namespace std;
const int N=210,inf=0x3f3f3f3f;
int love[N][N],ex_boy[N],ex_girl[N],slack[N],mat[N],n;
bool vis_girl[N],vis_boy[N];
bool dfs(int u){
vis_girl[u]=true;
for(int i=1;i<=n;i++){
if(vis_boy[i])continue;
int gap=ex_boy[i]+ex_girl[u]-love[u][i];
if(!gap){
vis_boy[i]=true;
if(!mat[i]||dfs(mat[i])){
mat[i]=u;return true;
}
}
else slack[i]=min(slack[i],gap);
}return false;
}
int KM(){
memset(mat,0,sizeof mat);
for(int i=1;i<=n;i++){
ex_girl[i]=love[i][1];
for(int j=2;j<=n;j++){
ex_girl[i]=max(love[i][j],ex_girl[i]);
}
}
for(int i=1;i<=n;i++){
memset(slack,0x3f,sizeof slack);
while(1){
memset(vis_boy,false,sizeof vis_boy);
memset(vis_girl,false,sizeof vis_girl);
if(dfs(i))break;
int d=inf;
for(int j=1;j<=n;j++)
if(!vis_boy[j])d=min(slack[j],d);
for(int j=1;j<=n;j++){
if(vis_girl[j])ex_girl[j]-=d;
if(vis_boy[j])ex_boy[j]+=d;
else slack[j]-=d;
}
}
}
int res=0;
for(int i=1;i<=n;i++)
res+=love[mat[i]][i];
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>love[i][j];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x;cin>>x;
love[j][i]*=x;
}
}
cout<<KM()<<endl;
return 0;
}