二分图最大权完美匹配(KM算法)

二分图最大匹配是寻找最大匹配数,用匈牙利算法。当连接的边带有权值时,要寻找匹配后权值和最大的方案,且保证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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值