交错树
如果从某个左部节点出发寻找增广路失败,那么在dfs的时候,所有经过的节点和边,共同构成一棵交错树
顶标
给左部节点一个整数值Ai,右部节点一个整数值Bj,满足Ai+Bj>=w(i,j) 初始时Ai=max{ w(i,j) } , Bj=0
相等子图
二分图中所有节点和满足Ai+Bj=w(i,j)的边构成子图
定理 若相等子图中存在完备匹配,该匹配即为二分图最大带权匹配
- 一个匹配失败的左部节点形成一个交错树T
- 右部节点通过匹配边访问左部节点
- 左部节点通过非匹配边访问右部节点
- 为了让该失配节点成功匹配,需要增加匹数
- 由于右部节点访问到的点是固定的,所以只能通过左部节点增加匹数
- 假如把T中所有左部节点的顶标Ai减少一个数值,右部节点顶标Bi增加一个数值,访问到的节点有何变化
- 对于T中匹配边,显然没有变化
- 对于T中非匹配边,如果i,j都在T中,显然没有变化
- 如果i在T中,j不在T中,则Ai+Bj减少,即以前从i访问不到的点j现在可能访问到了
- 为保证顶标符合条件,我们要找出最小的Ai+Bj-w(i,j),作为(6)中减去的数值
- 这样不断减小一些Ai+Bj的值,直到达到完备匹配,即为最大带全匹配
Code
bool dfs(int x){
la[x]=true;
rep(y,1,n) if(!lb[y])
if(a[x]+b[y]==w[x][y]){
lb[y]=1;
if(!match[y]||dfs(match[y])){
match[y]=x;
return true;
}
}
else delta=min(delta,a[x]+b[y]-w[x][y]);
return false;
}
int KM(){
rep(i,1,n){
a[i]=-inf,b[i]=0;
rep(j,1,n) a[i]=max(a[i],w[i][j]);
}
rep(i,1,n) while(true){
memset(la,false,sizeof(la));
memset(lb,false,sizeof(lb));
delta=inf;
if(dfs(i))break;
rep(i,1,n)a[i]-=delta,b[i]+=delta;
}
int ans=0;
rep(i,1,n)ans+=w[match[i]][i];
return ans;
}