ix35
2020-05-28 21:15:33
之前写的有锅,现在修正并整理,重发一下。
对于给定二分图
注意,如果给定的
为了求解这个问题,让我们再次借助线性规划工具:
其中
仍旧将它对偶,得到:
我们将对偶问题中的变量
最小顶标和问题:给定二分图,给每个点一个顶标,使得每条边两端点的顶标和不小于边权,且所有点顶标和最小。
不过,再次我们需要做一些补充:
接下来我们将介绍 KM 算法,KM 算法通过解决最小顶标和问题,可以求出给定二分图的一组权值和最大的完美匹配。
在叙述算法过程前,先说说怎么将原先的问题(最大权匹配)转化成最大权完美匹配:
我们将左部点
相等子图定义为所有满足
命题:相等子图中若存在完美匹配,则这组完美匹配是原图的一个最大权完美匹配。
证明:考虑这组完美匹配的边权和,根据相等子图定义应当是
接下来考虑:如果相等子图中没有完美匹配,尝试通过调整顶标使得相等子图中的最大匹配变大,从而最终形成完美匹配。
假设此时左侧有点
所以这么做不会影响原先匹配,但可以增加新的边进入相等子图,从而继续增广,而我们只要取
于是我们首先任意设定初始合法顶标(如右部点都为
考虑优化这一算法,我们下面介绍一种 slack 优化的基于 BFS 的 KM 算法。
在每次加入一个左部点,尝试增广时,模拟匈牙利算法求增广路的过程,而对于右部每个点
当我们访问到一个左部点时,先用它更新所有右部点的
下一个访问的左部点将是刚才取出的右部点的匹配点,这就是一个寻找增广路的过程,而当那个右部点是非匹配点时,我们已经找到了一条增广路。
重复上述过程,依次加入所有左部点,最后就求出了最小顶标和问题的解,也就是最大权完美匹配的方案。
void bfs (int x) {
memset(vis,0,sizeof(vis));
memset(s,0x3f,sizeof(s));
memset(pre,0,sizeof(pre));
cur=pn=0,mat[0]=x;
while (1) {
x=mat[cur];
ll dis=INF;
vis[cur]=1;
for (int i=n+1;i<=n+n;i++) {
if (vis[i]) {continue;}
ll tmp=w[i]+w[x]-d[x][i-n];
if (tmp<s[i]) {s[i]=tmp,pre[i]=cur;}
if (s[i]<dis) {dis=s[i],pn=i;}
}
w[mat[0]]-=dis,w[0]+=dis;
for (int i=n+1;i<=n+n;i++) {
if (vis[i]) {w[i]+=dis,w[mat[i]]-=dis;}
else {s[i]-=dis;}
}
cur=pn;
if (!mat[cur]) {break;}
}
while (cur) {
mat[cur]=mat[pre[cur]];
cur=pre[cur];
}
return;
}