博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
图的最大匹配算法
阅读量:5827 次
发布时间:2019-06-18

本文共 4888 字,大约阅读时间需要 16 分钟。

定义:在一个无向图中,定义一条边覆盖的点为这条边的两个端点。找到一个边集S包含最多的边,使得这个边集覆盖到的所有顶点中的每个顶点只被一条边覆盖。S的大小叫做图的最大匹配。

二分图的最大匹配算法:设左边集合为A集合,有边集合为B集合。二分图最大匹配常用的有两种方法。

(1)第一种方法叫做匈牙利算法。这个方法依次枚举A中的每个点,试图在B集合中找到一个匹配。对于A集合中一点x,假设B集合中有一个与其相连的点y,若y暂时还没有匹配点,那么x可以和y匹配,找到;否则,设y已经匹配的点为z(显然z是A集合中的一个点),那么,我们将尝试为z找到一个除了y之外的匹配点,若找到,那么x可以和y匹配,否则x不能与y匹配。

我们以下图为例说明匈牙利匹配算法。

step1:从1开始,找到右侧的点4,发现点4没有被匹配,所以找到了1的匹配点为4 。得到如下图:

step2:接下来,从2开始,试图在右边找到一个它的匹配点。我们枚举5,发现5还没有被匹配,于是找到了2的匹配点,为5.得到如下图:

step3:接下来,我们找3的匹配点。我们枚举了5,发现5已经有了匹配点2。此时,我们试图找到2除了5以外的另一个匹配点,我们发现,我们可以找到7,于是2可以匹配7,所以5可以匹配给3,得到如下图:

此时,结束,我们得到最大匹配为3。

(2)第二种方法叫做Hopcroft-Karp算法。这个算法大致思想与第一个方法相同。不同之处在于,这个方法每次找到一组互不相交的增广路径。我们用下面的例子说明。

step1:我们从所有未找到增广路径的点,也就是1,2,3,4开始,找增广路径,我们找到了1->2,3->3两条(左边的红线表示增广路径),然后沿着这些增广路径求匹配点,得到了右边的图,即1匹配2,3匹配3。

一般图的最大匹配算法:我们从一个没有匹配的节点s开始,使用BFS生成搜索树。每当发现一个节点u,如果u还没有被匹配,那么就可以进行一次成功的增广,即s匹配u;否则,我们就把节点u和它的配偶v一同接到树上,之后把v丢进队列继续搜索。我们给每个在搜索树上的点一个类型:S或者T。当u把它的配偶v扔进队列的时候,我们把u标记为T型,v标记为S型。于是,搜索树的样子是这样的:

否则,我们找到了一个长度为奇数的环,如下图所示

 就要进行一次“缩花”的操作!所谓缩花操作,就是把这个环缩成一个点。这个图缩花之后变成了5个点(一个大点,或者叫一朵花,加原来的4个点):缩点完成之后,还要把原来环里面的T型点统统变成S型点,如下图

之所以能缩成一个点,是因为,一个长度为奇数的环(例如上图中的s-b-d-j-f-c-a-s),如果我们能够给它中的任意一个点找一个出度,那么环中的其他点正好可以配成对,这说明,每个点的出度都是等效的。这就是缩点的思想来源。
 
 

 

无向图最大匹配实现:

1 #define MAXN 250  2   3 class GraphMaxMatch  4 {  5 private:  6     int que[MAXN],queHead,queTail;  7     bool g[MAXN][MAXN];  8     bool inque[MAXN],inblossom[MAXN];  9     int match[MAXN],pre[MAXN],S[MAXN]; 10     int n; 11  12     void addQueEle(int u) 13     { 14         if(inque[u]) return; 15         inque[u]=1; 16         que[queTail++]=u; 17         if(queTail==MAXN) queTail=0; 18     } 19     int popQueEle() 20     { 21         int u=que[queHead++]; 22         if(queHead==MAXN) queHead=0; 23         return u; 24     } 25  26     int findancestor(int u,int v) 27     { 28         int visit[MAXN]; 29         memset(visit,0,sizeof(visit)); 30         while(1) 31         { 32             u=S[u]; 33             visit[u]=1; 34             if(match[u]==-1) break; 35             u=pre[match[u]]; 36         } 37         while(1) 38         { 39             v=S[v]; 40             if(visit[v]) break; 41             v=pre[match[v]]; 42         } 43         return v; 44     } 45     void reset(int u,int root) 46     { 47         int v; 48         while(u!=root) 49         { 50             v=match[u]; 51             inblossom[S[u]]=1; 52             inblossom[S[v]]=1; 53             v=pre[v]; 54             if(S[v]!=root) pre[v]=match[u]; 55             u=v; 56         } 57     } 58  59     void contract(int u,int v) 60     { 61         int root=findancestor(u,v); 62         memset(inblossom,0,sizeof(inblossom)); 63         reset(u,root); reset(v,root); 64         if(S[u]!=root) pre[u]=v; 65         if(S[v]!=root) pre[v]=u; 66         for(int i=1;i<=n;i++) if(inblossom[S[i]]) 67         { 68             S[i]=root; 69             addQueEle(i); 70         } 71     } 72  73     bool BFS(int start) 74     { 75         for(int i=1;i<=n;i++) pre[i]=-1,inque[i]=0,S[i]=i; 76         queHead=queTail=0; 77         addQueEle(start); 78         while(queHead!=queTail) 79         { 80             int u=popQueEle(); 81  82             for(int v=1;v<=n;v++) if(g[u][v]&&S[v]!=S[u]&&match[u]!=v) 83             { 84                 if(v==start||match[v]!=-1&&pre[match[v]]!=-1) 85                 { 86                     contract(u,v); 87                 } 88                 else if(pre[v]==-1) 89                 { 90                     pre[v]=u; 91                     if(match[v]!=-1) addQueEle(match[v]); 92                     else 93                     { 94                         u=v; 95                         while(u!=-1) 96                         { 97                             v=pre[u]; 98                             int tmp=match[v]; 99                             match[u]=v;100                             match[v]=u;101                             u=tmp;102                         }103                         return true;104                     }105                 }106             }107         }108         return false;109     }110 public:111     /**112     vertexNum: vertex number113     G[1~vertexNum][1~vertexNum]: edge relation114     */115     vector
> calMaxMatch(116 int vertexNum,const bool G[MAXN][MAXN])117 {118 n=vertexNum;119 for(int i=1;i<=n;i++)120 {121 for(int j=1;j<=n;j++) g[i][j]=G[i][j];122 }123 memset(match,-1,sizeof(match));124 for(int i=1;i<=n;i++) if(match[i]==-1) BFS(i);125 126 vector
> ans;127 for(int i=1;i<=n;i++)128 {129 if(match[i]!=-1&&match[i]>i)130 {131 ans.push_back(make_pair(i,match[i]));132 }133 }134 return ans;135 }136 };

 

转载于:https://www.cnblogs.com/jianglangcaijin/p/6035950.html

你可能感兴趣的文章
3D实时渲染中的BSP树和多边形剔除
查看>>
Frank Klemm's Dither and Noise Shaping Page: Dither and Noise Shaping In MPC/MP+
查看>>
网络抓包的部署和工具Wireshark【图书节选】
查看>>
Redis在Windows+linux平台下的安装配置
查看>>
Maven入门实战笔记-11节[6]
查看>>
Local declaration of 'content' hides instance variable
查看>>
ASP.NET中 HTML标签总结及使用
查看>>
Linux下日志系统的设计
查看>>
爬虫IP被禁的简单解决方法——切换UserAgent
查看>>
php生成word,并下载
查看>>
紫书 习题8-11 UVa 1615 (区间选点问题)
查看>>
asp.net mvc学习(Vs技巧与Httpcontext)
查看>>
float数据在内存中是怎么存储的
查看>>
dedecms 修改标题长度可以修改数据库
查看>>
Matplotlib学习---用matplotlib画直方图/密度图(histogram, density plot)
查看>>
MySQL案列之主从复制出错问题以及pt-slave-restart工具的使用
查看>>
linux 查看剩余内存数
查看>>
测试人员容易遗漏的隐藏缺陷
查看>>
maven+SpringMVC搭建RESTful后端服务框架
查看>>
一本书的摘录
查看>>