KM
算法思想
设有A[],B[]二分图,贪心的去找与A[i]的最大匹配,若没有找到就增加边,直到找到最优为止;
实现步骤
1.初始化l[],r[],数组,设置顶标,即找到与A[i]最大匹配的B[],存在l[]数组中,r[]数组初始化为0;
2.dfs()寻找增广路径(最大匹配),和匈牙利类似;
a.如果找到匹配点就进入下一匹配点;
b.如果没有找到就增加新的路径;
注意:
在实现最小匹配时,将两点的权值变为负值,初始化l[]数组时需要将其设为-INF,其余不变;
代码
例题:HDU2255
#include <bits/stdc++.h>
using namespace std;
const int N=302;
const int inf=0x3f3f3f3f;
int mp[N][N],l[N],r[N],match[N],slack[N];//mp[]存边,l[],r[]存改变后的期望值,match[]存相连的点,slack[]存最小期望值用于新建边;
bool pl[N],pr[N];
int n;
bool dfs(int x)
{
pl[x]=true;
for(int i=1;i<=n;++i){
if(pr[i]) continue;
int t=l[x]+r[i]-mp[x][i]; //期望的差值,用于判定是否可以匹配和建新边;
if(!t){
pr[i]=true;
if(match[i]==-1||dfs(match[i])){
match[i]=x;
return true;
}
}
else{
slack[i]=min(slack[i],t);
}
}
return false;
}
int KM()
{
memset(match,-1,sizeof(match));
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
l[i]=max(l[i],mp[i][j]); //初始化l[]数组;
}
}
for(int i=1;i<=n;++i){
memset(slack,inf,sizeof(slack)); //初始化slack,最小期望值;
while(true){
memset(pl,false,sizeof(pl));
memset(pr,false,sizeof(pr));
if(dfs(i)) break;
int minn=inf; //没有找到匹配点,需要建立新边;
for(int j=1;j<=n;++j){
if(!pr[j]) minn=min(minn,slack[j]);
}
for(int j=1;j<=n;++j){
if(pl[j]) l[j]-=minn;
if(pr[j]) r[j]+=minn;
else slack[j]-=minn;
}
}
}
int ans=0;
for(int i=1;i<=n;++i){
ans+=mp[match[i]][i];
}
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
scanf("%d",&mp[i][j]);
}
}
printf("%d\n",KM());
}
return 0;
}