匈牙利算法
问题
解决二分图最大匹配问题。
思想
不断寻找原有匹配 \(M\) 的增广路径以增大匹配数。
对每次匹配左边点 \(x\),不断通过右边点 \(y\) 跳到左边 \(y\) 的对象点 \(x*\),直至 \(x\) 右边点 \(y\) 没有对象。
代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=509;
int n,m,e,ans;
int vis[N],t[N];
vector<int>v[N];
bool dfs(int x)
{
for(auto y:v[x])
if(!vis[y])
{
vis[y]=1;
if(!t[y]||dfs(t[y]))
{
t[y]=x;
return 1;
}
}
return 0;
}
int main()
{
cin>>n>>m>>e;
for(int i=1;i<=e;i++)
{
int x,y;
cin>>x>>y;
if(x>n||y>m) continue;
v[x].pb(y);
}
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
cout<<ans<<endl;
return 0;
}
KM算法
问题
求二分图最大权完美匹配。
步骤
-
初始化可行顶标的值 (设定 \(lx,ly\) 的初始值)
\(lx_i=max(w[i][j])\),\(ly_i=0\) -
用匈牙利算法寻找相等子图的完备匹配
完备匹配满足 \(lx[x]+ly_[y]=w[x][y]\) -
若未找到增广路则修改可行顶标的值
对路径上的左节点 \(lx[i]-=minn\) ,对路径上的右节点 \(ly[i]+=minn\)
而 \(minn\) 为路径上左节点与非路径上右节点 \(lx[x]+ly_[y]-w[x][y]\) 的最小值 -
重复(2)(3)直到找到相等子图的完备匹配为止
个人理解
初始顶标和可能是最大权,在之后每次匹配下会不断发生冲突,因此需解决在已有匹配下调整匹配使得匹配最优,所用思想即为贪心
每次匹配下,若有冲突,路径上左节点有 \(m\) 个,右节点 \(m-1\) 个,最优解决方法即为:对路径上 \(m\) 个左节点 \(-minn\) ,对路径上 \(m-1\) 个右节点 \(+minn\) 。
这样,相当于左顶标流向右顶标,保证了路径上 \(lx[x]+ly_[y]=w[x][y]\) ,也使得路径上某个左节点(取 \(minn\) 的节点)可以与非路径上右节点(取 \(minn\) )匹配。同时,总的顶标和仅减小一个 \(minn\),权值也最优。
博文推荐
KM算法详解+模板 -本文没有给出KM算法的原理,只是模拟了一遍算法的过程。明白易懂算法流程
二分图的最佳完美匹配——KM算法 -详细证明
代码
#include<bits/stdc++.h>
using namespace std;
const int N=29;
int n,minn,ans;
int p[N][N],q[N][N],lx[N],ly[N],visx[N],visy[N],t[N];
bool dfs(int x)
{
visx[x]=1;
for(int y=1;y<=n;y++)
if(!visy[y])
{
int temp=lx[x]+ly[y]-p[x][y];
if(!temp)
{
visy[y]=1;
if(!t[y]||dfs(t[y]))
{
t[y]=x;
return true;
}
}
else minn=min(minn,temp);
}
return false;
}
void KM()
{
for(int i=1;i<=n;i++)
while(1)
{
minn=1e9+7;
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(dfs(i)) break;
for(int j=1;j<=n;j++)
{
if(visx[j]) lx[j]-=minn;
if(visy[j]) ly[j]+=minn;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>p[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
cin>>q[i][j];
p[j][i]*=q[i][j];
lx[j]=max(lx[j],p[j][i]);
}
KM();
for(int i=1;i<=n;i++)
ans+=p[t[i]][i];
cout<<ans<<endl;
return 0;
}
浙公网安备 33010602011771号