二分图(二)——匈牙利算法

匈牙利算法是一种用贪心算法来求二分无权图的最大匹配算法,首先介绍一下有关于匈牙利算法的基本概念:
交替路:依次经过未匹配边,匹配边,未匹配边……的路。
增广路:实际上增广路里包含着交替路:从一个未匹配点开始,走交替路,停止于另外一个未匹配点。
增广路的性质:
1.增广路的边数必定为奇数条边,且有其未匹配边的边数比匹配边数多一条。
2.增广路除起点和终点外,其路径上的所有点都是匹配点。
通过不断改进匹配(也就是反选增广路,也就是……把未匹配边和匹配边身份对调)
最大匹配:当且仅当一个二分图中没有了任何一条增广路,则该二分图有了最大匹配。
下面开始匈牙利算法的讲解:
如果当前存在未匹配点,那么则将该点(叫做点a好了)和其中一个与之相连的未匹配点进行匹配,如果与该点相连的都是匹配点,那么看在该点之前的点(点b)能不能(通过改变它的匹配边)把点a和点b的公共匹配点让给该点并将点b的仍有匹配边,如果能改变其匹配边,则进行改变,然后把点a与让出来的那个点进行匹配,若之前的都不能改变,则直接进行下一个点的选择,放弃点a。
比方说这个图:
这是初始图
进行第一步的操作后:
这是第一步
进行第二步的操作后:
这是第二步
结束算法。
总之,匈牙利算法是秉承着一种“能连就连,能不改就不改”的原则的。
这个似乎……跟增广路没有多少联系啊……
但是实际上是有的。
在更改当前点的选择时(也就是说,若A有两个未匹配边与之相连,其另一端点分别是B,C,但B已经有了边与之匹配(假设B–D),所以A—C,这个定义为更改当前点的选择),我们发现,实际上D—–B——A——C是一条“逆”增广路(也就是,已经改进匹配的增广路)。
更改当前点的选择
而在更改之前的点的选择时(若之前有A有两个未匹配边与之相连,其另一端点分别是B,C,且B,C都没有被匹配过,然后A把B匹配了,现在遍历到D点,D点只与B相连,那么则删掉A与B的匹配,将A与C匹配起来。再将B与D进行匹配。),会发现之前的D——-B——–A———–C是一条增广路,而在改变A的选择并重新选择B时,实际上是增广路的改进匹配!
更改之前点的选择
时间复杂度:
左边的点的个数n*总边数m;
O(nm)。
而其代码有DFS和BFS两种,这里过几日重新编辑时应该会给出。
但是似乎DFS更加常用更加易懂……这里给出DFS代码:

#include <iostream>
#include <ctime>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iomanip>
#include <stack>
using namespace std;
const int maxn=1005;
int n,m;
bool used[maxn];
int lt[maxn][maxn];
int link[maxn];
bool dfs(int u){
    for(int i=1;i<=n;i++){
        if(lt[u][i]&&!used[i]){
            used[i]=1;
            if(link[i]==-1||dfs(link[i])){
                link[i]=u;
                return 1;
            }
        }

    }
    return 0;
}
int main (){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d",&u,&v);
        lt[u][v]==1;//无论是有向图还是无向图我们都这样存,因为转换成二分图求最大匹配边数时其匹配边数都相等\
        而如果习惯无向图存两次的话,那么最大匹配数要>>1; 
    }
    int ans=0;
    memset(link,-1,sizeof(link));
    for(int i=1;i<=n;i++){
        memset(used,0,sizeof(used));
        if(dfs(i))ans++;
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值