KM算法
首先了解问题:也就是最大权值匹配;
二分图里,边带了权值,求整幅图里匹配最大/最小的权值
因为接触匈牙利算法的时候看的是找对象系列的博文,所以也自己写一发找对象的博文吧;
算法背景:
信息学院计算机某班级有5位玉树凌风的男子:小诸,小包,小许,小应,小章;
外语学院英语系某班级有5位国色天香的女子:小四,小雨,小美,小丽,小英;
名称不重要!强行变成X,Y,233333
然后5位男子对5位女子都有一个好感度,见表;
| y0 | y1 | y2 | y3 | y4 |
x0 | 4 | 1 | 6 | 2 | 3 |
x1 | 5 | 0 | 3 | 7 | 6 |
x2 | 2 | 3 | 4 | 5 | 8 |
x3 | 3 | 4 | 6 | 3 | 4 |
x4 | 4 | 6 | 5 | 8 | 6 |
现在要给这几个男孩子找女朋友~使得他们所有好感度和最大。
KM算法步骤:
初始情况下把好感度都放在男子的集合上,即
LX0=6;
LX1=7;
LX2=8;
LX3=6;
LX4=8;
然后枚举X集合元素去匹配,如果匹配到的边wij=LXi+LYj,并且j还不存在配偶,那么,嘿嘿嘿,就让他们牵手~(✿◡‿◡)
比如:在枚举X集合时,X0-Y2;X1-Y3;X2-Y4;
但是,有些女孩子很受欢迎,突然到了X3,X3最喜欢Y2,而Y2已经有X0喜欢了,窝们的目的是让整个好感度最大;
那么就会有两种情况:
① :我让X0再去找一个,然后让X3-Y2;
② :我让X3另外去选,然后还是让X0-Y2;
那么窝们怎么能随随便便让男孩子另外去找一个呢,窝们其实很容易想到一个方案就是让一个男孩子去找另外一个女孩子使得好感度比原先大的好感度降低的越少,那么这样还是会让总好感度最大;
具体的方法就是窝们可以开一个差值数组代表最大的好感度和其他好感度的差;
然后每次在寻找女朋友的时候将这个差值数组进行更新;
每次扫完女孩子以后,如果没有找到心仪对象,
找到那个最小差值d
把LX数组种那些有矛盾的男孩子的值-=d;
把LY数组种那些引起矛盾的女孩的值+=d;
观察窝们说一个男孩子找到女朋友的条件是:如果匹配到的边wij=LXi+LYj,并且j还不存在配偶,那么,嘿嘿嘿,就让他们牵手~(✿◡‿◡)
这样就能保证在下次找配偶的过程中
1.LX-=d,当再次对当前有矛盾的男孩子寻找时可能一下子找到那个好感度减少最少的女孩,因为LX减少,然而LY不变,所以一旦满足条件,就返回true;
2.LY+=d,引起矛盾的那个女生还是照样引起矛盾或者在之前就被寻找过在下次寻找到时不寻找;
3.在对出现矛盾的那个女生的本来配好的男朋友进行寻找时,同理1的情况,找到那个好感度减少最少的女孩且满足情况就返回;
这样算法流程说明(个人理解):
LX的作用是维护权值减少最少,LY的作用是维护权值最大的依旧最大;
教科书术语:
(1) 令(u,v)是一个覆盖,如对于每个j,ui=max(wij),vi=0;
(2) 在Ku*v中找出一个最大匹配M。如果M是一个完美匹配,停止并将M作为最大权值匹配返回。否则,令Q是Ku*v中大小为M的一个定点覆盖。设R=Q∩X,T=Q∩Y。令e=min{ui+vj-wij,xi∈X-R,yi∈Y-T}。
(3) 对于xi∈X-R,从ui中减去e.对yi∈T,把e加到vj上,形成一个新的相等子图,转向步骤(2);
贴一发模板:
bool Findpath(int u)
{
vx[u]=1;
for(int i=1;i<=n;i++)
{
if(vy[i]) continue;
int temp=lx[u]+ly[i]-ma[u][i];
if(temp==0)
{
vy[i]=1;
if(match[i]==-1||Findpath(match[i]))
{
match[i]=u;
return true;
}
}
else if(d>temp)
d=temp;
}
return false;
}
int KM()
{
memset(match,-1,sizeof(match));
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
lx[i]=max(ma[i][j],lx[i]);
for(int i=1;i<=n;i++)
{
while(1)
{
memset(vx,0,sizeof(vx));
memset(vy,0,sizeof(vy));
d=0x3f3f3f3f;
if(Findpath(i)) break;
for(int j=1;j<=n;j++)
{
if(vx[j]) lx[j]-=d;
if(vy[j]) ly[j]+=d;
}
}
}
int res=0;
for(int i=1;i<=n;i++)
{
res+=ma[match[i]][i];
}
return res;
}