二分图最大匹配是寻找最大匹配数,用匈牙利算法。当连接的边带有权值时,要寻找匹配后权值和最大的方案,且保证A集合中的点均有B中的点能匹配。此时问题就转化为二分图最大权完美匹配。
KM算法核心为:为每一点添加顶标,在顶标的限制下用匈牙利算法处理出最大匹配数,若最大匹配数=n,则达到最优解,输出。否则修改顶标,再用匈牙利算法处理,如此重复。
时间复杂度:O(n3)
参考:二分图最大权完美匹配 题解
(转载模板)
【模板】二分图最大权完美匹配
#include<bits/stdc++.h>
#define ll long long
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Rep(i,l,r) for(int i=(l);i<=(r);i++)
using namespace std;
template<typename T>//边权的类型
struct KM{
const T INF = 1e18;
static const int N = 500;
int mb[N+7],vb[N+7],p[N+7];
//注意,mb表示:完美匹配下与右部第i个点相匹配的**左部点的编号**.
T ka[N+7],kb[N+7],c[N+7],e[N+7][N+7];
void init(int n){
for(int a=1;a<=n;a++){
for(int b=1;b<=n;b++){
e[a][b]=-INF;
}
}
}
void add(int u,int v,T w){
e[u][v]=max(e[u][v],w);
}
void Bfs(int u,int n){
int a,v=0,vl=0;
T d;
for(int i=1;i<=n;i++){p[i]=0;c[i]=INF;}
mb[v]=u;
do {
a=mb[v],d=INF,vb[v]=1;
for(int b=1;b<=n;b++)if(!vb[b]){
if(c[b]>ka[a]+kb[b]-e[a][b])
c[b]=ka[a]+kb[b]-e[a][b],p[b]=v;
if(c[b]<d) d=c[b],vl=b;
}
for(int b=0;b<=n;b++)
if(vb[b]) ka[mb[b]]-=d,kb[b]+=d;
else c[b]-=d;
v=vl;
} while(mb[v]);
while(v) mb[v]=mb[p[v]],v=p[v];
}
T km(int n){
for(int i=1;i<=n;i++) mb[i]=ka[i]=kb[i]=0;
for(int a=1;a<=n;a++){
for(int b=1;b<=n;b++) vb[b]=0;
Bfs(a,n);
}
T res=0;
for(int b=1;b<=n;b++) res+=e[mb[b]][b];
return res;
}
};
int n,m;
KM<ll>km;
//Main
//左右n个节点,m条边带权边
//输入n,m和m条边
int main(){
IOS
cin>>n>>m;
km.init(n);
for(int i=1;i<=m;i++){
ll u,v;
double w;//m条带权边
cin>>u>>v>>w;
km.add(u,v,w);
}
printf("%lld\n",km.km(n));
for(int u=1;u<=n;u++) printf("%d ",km.mb[u]);puts("");
return 0;
}