匈牙利算法及其模版

二分图最大匹配的匈牙利算法:

    二分图是这样一个图,它的顶点可以分类两个集合X和Y,所有的边关联在两个顶点中,恰好一个属于集合X,另一个属于集合Y。

 

最大匹配: 图中包含边数最多的匹配称为图的最大匹配。 

 

完美匹配: 如果所有点都在匹配边上,称这个最大匹配是完美匹配。

 

最小覆盖:在一个二分图上用最少的点(x 或 y 集合的都行),让每条连接两个点集的边都至少和其中一个点关联。根据konig定理:二分图的最小顶点覆盖数等于最大匹配数。

要覆盖M条匹配边,至少需要M个点。

 

最小路径覆盖:用尽量少的不相交简单路径(连着n条边)覆盖有向无环图G的所有结点,且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,那么恰好可以经过图中的每个顶点一次且仅一次);解决此类问题可以建立一个二分图模型。把所有顶点i拆成两个:X结点集中的i和Y结点集中的i',如果有边i->j,则在二分图中引入边i->j',设二分图最大匹配为m,则结果就是n-m。

 

最大独立集问题:在N个点的图G中选出m个点,使这m个点两两之间没有边(没有某种关系).求m最大值.如果图G满足二分图条件,则可以用二分图匹配来做.最大独立集点数 = N - 最大匹配数

 

二分图最大匹配问题的匈牙利算法:

#include<iostream>
using namespace std;
const int Max = 405;
 
int n, m;                       //  二分图中x和y中点的数目
int link[Max];                  //  link[x]记录当前与y节点相连的x的节点。
bool map[Max][Max], vis[Max];   //  map[i][j]记录连接x和y的边,如果i和j之间有边则为1,否则为0。
bool dfs(int u){                //  dfs实现,u表示现在在寻求匹配y的点x。
    for(int i = 1; i <= m; i ++)
        if(!vis[i] && map[u][i]){
            vis[i] = true;
            if(link[i] == -1 || dfs(link[i])){    //  条件:点i还没匹配,或者link[i]找到新的匹配。
                link[i] = u;
                return true;
            }
        }
    return false;
}
 
int MaxMatch(){
    int i,num = 0;
    memset(link, -1, sizeof(link));
    for(i = 1;i <= n; i ++){
        memset(vis, 0, sizeof(vis));
        if(bfs(i)) num++;
    }
    return num;
}


 

算法思想:

算法的思路是不停的找增广轨,并增加匹配的个数,增广轨顾名思义是指一条可以使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条"交错轨",也就是说这条由图的边组成的路径,它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有..最后一条边没有参与匹配,并且始点和终点还没有被选择过.这样交错进行,显然他有奇数条边.那么对于这样一条路径,我们可以将第一条边改为已匹配,第二条边改为未匹配...以此类推.也就是将所有的边进行"反色",容易发现这样修改以后,匹配仍然是合法的,但是匹配数增加了一对.另外,单独的一条连接两个未匹配点的边显然也是交错轨.可以证明,当不能再找到增广轨时,就得到了一个最大匹配.这也就是匈牙利算法的思路.

下面这个博客比较详细的讲解匈牙利算法的过程

http://hi.baidu.com/microprocessor/blog/item/89f16c2f247e75564fc2260b.html

还有一个匈牙利的模版也copy过来。

/*===========================================================*\
G为图,M为图中的一个匹配,从X集合到Y集合找一个最大匹配
交互道路:对于G的一个匹配M, G中属于M与不属于M的边交替出现的道路
称为一条交互道路.
可增广道路:设P是关于匹配M的一条交互道路,如果P的两个端点是关于
M的非饱和点,那么称P是一条可增广道路。

\*===========================================================*/
int uN, vN; // uN为集合X的个数, vN为集合Y的个数,要初始化!!!
bool g[MAXN][MAXN]; // g[i][j] 表示xi与yj相连
int xM[MAXN], yM[MAXN]; // 输出量
bool chk[MAXN]; // 辅助量检查某轮y[v]是否被check
bool SearchPath(int u){
int v;
for(v = 0; v < vN; v++)
if(g[u][v] && !chk[v])//取u的临界点集合为Y,并且Y没有被访问过
{
chk[v] = true;
if(yM[v] == -1 || SearchPath(yM[v]))//若Y中有非饱和点(-1),则找到一条增广路径;
//若Y已饱和,则找到在匹配M中的边(x,y),x的集合加到X中,
//延长路径(-1->饱和Y->饱和X->(找非饱和点)),SearchPath(yM[v])找到Y中的一个非饱和点
{
yM[v] = u; xM[u] = v;//进行值更新
return true ;
}
}
return false ;
}
int MaxMatch(){
int u, ret = 0 ;
memset(xM, -1, sizeof (xM));//-1表示为非饱和点
memset(yM, -1, sizeof (yM));//-1表示为非饱和点
for(u = 0; u < uN; u++)
if(xM[u] == -1){// 在集合X中找任一非饱和点进行一次增广路径的查找
memset(chk, false, sizeof (chk));
if(SearchPath(u)) ret++;//如果找到一条增广路径,最大匹配的个数加1
}
return ret;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值