模板总结——二分图最大权匹配

基本概念

完全二分图:两侧顶点集大小相等的二分图

完备匹配:所有顶点都是匹配点的匹配

最大权匹配:在带权的完全二分图中,匹配边的权值和最大的完美匹配。

相等子图:由原图的点集和边权等于顶点标号的和的边集组成的图。

KM算法原理

设左侧顶点的顶标集为{Ui},右侧顶点的顶标集为{Vj}。

定理:如果含有顶标的二分图的相等子图存在完备匹配,则该匹配就是原图的最大权匹配。

KM算法通过不断地调整{Ui}和{Vj},直到其相等子图存在一个完备匹配,然后用匈牙利算法求出这个完备匹配。

KM算法流程

流程:
(1)初始化可行顶标的值
(2)用匈牙利算法寻找完备匹配
(3)若未找到完备匹配,则修改可行顶标的值
(4)重复(2)(3)直到找到相等子图的完备匹配为止

伪代码:
(1)左侧顶标设为所有相连边的最大边权,右侧顶标设为0
(2)对应图左侧的每一顶点u
(2.1)清空所有标记
(2.2)如果Find(u),继续
(2.3)否则,修改顶标集
(2.3.1)d=min{Ui+Vj-Wij},对于所有左侧被标记右侧未标记的边
(2.3.2)对于所有被标记的点,左侧的顶标-=d,右侧的顶标+=d
(2.3.3)回到(2.1)
(3)根据输出的匹配算出最大权

模板

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=330;
int from[maxn],w[maxn][maxn];
int lx[maxn],ly[maxn],visx[maxn],visy[maxn],slack[maxn];
int nx,ny;
///定义slack[y]=min{lx[x]+ly[y]-w[x][y]},x已访问,y未访问

bool Find(int u)
{
    visx[u]=1;
    for(int v=0;v<ny;v++)if(!visy[v])
    {
        int tmp=lx[u]+ly[v]-w[u][v];
        if(tmp==0)
        {
            visy[v]=1;
            if(from[v]==-1 || Find(from[v]))
            {
                from[v]=u;
                return true;
            }
        }
        else slack[v]=min(slack[v],tmp);
    }
    return false;
}
int KM()
{
    for(int i=0;i<nx;i++)
    {
        ly[i]=0;
        lx[i]=-INF;
        for(int j=0;j<ny;j++)
            lx[i]=max(lx[i],w[i][j]);
    }
    memset(from,-1,sizeof(from));
    for(int u=0;u<nx;u++)
    {
        for(int i=0;i<ny;i++) slack[i]=INF;
        while(true)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(Find(u)) break;
            int d=INF;
            for(int i=0;i<ny;i++)if(!visy[i])
                d=min(d,slack[i]);
            for(int i=0;i<nx;i++)if(visx[i])
                lx[i]-=d;
            for(int i=0;i<ny;i++)
            {
                if(visy[i]) ly[i]+=d;
                else slack[i]-=d;
            }
        }
    }
    int ans=0;
    for(int i=0;i<ny;i++)
        if(from[i]!=-1) ans+=w[from[i]][i];
    return ans;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        nx=ny=n;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",&w[i][j]);
        printf("%d\n",KM());
    }
    return 0;
}

模板题

奔小康赚大钱 HDU-2255

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值