注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Memento

这是一间记忆仓库~~

 
 
 

日志

 
 

二分图匹配  

2012-07-23 12:58:37|  分类: 学习小结 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
       前段时间系统的学习了一下二分图匹配,收获还是蛮大的,总算是把最大匹配、点覆盖、点独立、边覆盖什么的关系搞清楚了,基本的算法和定理都会了,剩下的就是建模问题了,这个只能慢慢积累了,最后写个小结。

一、二分图最大匹配
       定义:匹配是图中一些边的集合,且集合中任意两条边都没有公共点,所有的匹配中,边数最多的就是最大匹配。
       算法:用匈牙利算法可以在O(V*E)的复杂度内求出二分图的最大匹配,具体可以看byvoid神犇的blog,讲的很详细,不过想真正完全证明这个算法,得去看组合数学。

二、二分图最小点覆盖
       定义:点覆盖是图中一些点的集合,且对于图中所有的边,至少有一个端点属于点覆盖,点数最小的覆盖就是最小点覆盖。
       定理:最小点覆盖=最大匹配。
       简单证明:首先必然有,最小覆盖>=最大匹配。于是只要证明不等式可以取等号,我们可在最大匹配的基础上构造出一组点覆盖。对右边每一个未匹配的点进行dfs找增广路,标记所有dfs过程中访问到的点,左边标记的点+右边未标记的点就是这个图的一个点覆盖。因为对于任意一条边,如果他的左边没标记,右边被标记了,那么我们就可找到一条新的增广路,所以每一条边都至少被一个点覆盖。再来证明:最大匹配=左边标记的点+右边未标记的点。对于每条匹配边,只有一个点属于点覆盖。如果这条边在dfs过程中被访问了,那么就左端点属于点覆盖,右端点不属于,否则就有左端点不属于点覆盖,右端点属于点覆盖。除此之外,不可能存在其它的点属于最小覆盖了,不然就必然可以找到增广路。所以:左边标记的点+右边未标记的点=最大匹配,对于任意的二分图,我们总能在最大匹配的基础上构造出一组点数等于最大匹配的点覆盖,所以:最小点覆盖=最大匹配。

三、二分图最小边覆盖
       定义:边覆盖是图中一些边的集合,且对于图中所有的点,至少有一条集合中的边与其相关联,边数最小的覆盖就是最小边覆盖。
       定理:最小边覆盖=图中点的个数-最大匹配。
       简单证明:先贪心的选一组最大匹配的边加入集合,对于剩下的每个未匹配的点,随便选一条与之关联的边加入集合,得到的集合就是最小边覆盖,所以有:最小边覆盖=最大匹配+图中点的个数-2*最大匹配=图中点的个数-最大匹配。

四、二分图最大独立集
       定义:独立集是图中一些点的集合,且图中任意两点之间都不存在边,点数最大的就是最大独立集。
       定理:最大独立集=图中点的个数-最大匹配。
       简单证明:可以这样来理解,先把所有的点都加入集合,删除最少的点和与其关联的边使得剩下的点相互之间不存在边,我们就得到了最大独立集。所以有:最大独立集=图中点的个数-最小点覆盖=图中点的个数-最大匹配。

五、有向无环图最小不相交路径覆盖
       定义:用最少的不相交路径覆盖所有顶点。
       定理:把原图中的每个点V拆成Vx和Vy,如果有一条有向边A->B,那么就加边Ax-By。这样就得到了一个二分图,最小路径覆盖=原图的节点数-新图最大匹配。
       简单证明:一开始每个点都独立的为一条路径,总共有n条不相交路径。我们每次在二分图里加一条边就相当于把两条路径合成了一条路径,因为路径之间不能有公共点,所以加的边之间也不能有公共点,这就是匹配的定义。所以有:最小路径覆盖=原图的节点数-新图最大匹配。

六、有向无环图最小可相交路径覆盖
       定义:用最小的可相交路径覆盖所有顶点。
       算法:先用floyd求出原图的传递闭包,即如果a到b有路,那么就加边a->b。然后就转化成了最小不相交路径覆盖问题。

七、偏序集的最大反链
       定义:偏序集中的最大独立集。
       Dilworth定理:对于任意偏序集都有,最大独立集(最大反链)=最小链的划分(最小不相交路径覆盖)。
       通过Dilworth定理,我们就可以把偏序集的最大独立集问题转化为最小不相交路径覆盖问题了。

八、二分图带权最大匹配
       定义:每个边都有一组权值,边权之和最大的匹配就是带权最大匹配。
       算法:KM算法,复杂度为O(V^3)。具体就不说了,网上有不少资料。
       要注意的是,KM算法求的是最佳匹配,即在匹配是完备的基础上权值之和最大。这和带权最大匹配是不一样的,不过我们可以加入若干条边权为0的边使得KM求出来的最佳匹配等于最大权匹配。具体实现的时候最好用矩阵来存图,因为一般点的个数都是10^2级别,并且这样默认任意两点之间都存在边权为0的边,写起来很方便。如果要求最小权匹配,我们可以用一个很大数减去每条边的边权。

匈牙利算法模板:


int match[N],vis[N];

bool dfs(int rt){
for(edge *i=Adj[rt];i;i=i->nxt){
int v=i->v;
if(!vis[v]){
vis[v]=1;
if(match[v]==0||dfs(match[v])){
match[v]=rt;
return true;
}
}
}
return false;
}

int hungary(){
int ret=0;
memset(match,0,sizeof(match));
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(dfs(i)) ret++;
}
return ret;
}


KM算法模板:


int match[N],visx[N],visy[N],lx[N],ly[N],slk[N];

bool dfs(int x){
visx[x]=1;
for(int y=1;y<=m;y++)
if(!visy[y]){
int t=lx[x]+ly[y]-w[x][y];
if(t==0){
visy[y]=1;
if(match[y]==0||dfs(match[y])){
match[y]=x;
return true;
}
}
else
slk[y]=min(slk[y],t);
}
return false;
}

int KM(){
memset(match,0,sizeof(match));
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) lx[i]=max(lx[i],w[i][j]);
for(int i=1;i<=n;i++)
while(1){
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
for(int j=1;j<=m;j++) slk[j]=inf;
if(dfs(i)) break;
int d=inf;
for(int j=1;j<=m;j++) if(!visy[j]) d=min(d,slk[j]);
for(int j=1;j<=n;j++) if(visx[j]) lx[j]-=d;
for(int j=1;j<=m;j++) if(visy[j]) ly[j]+=d;
}
int ret=0;
for(int i=1;i<=n;i++) ret+=lx[i];
for(int i=1;i<=m;i++) if(match[i]) ret+=ly[i];
return ret;
}


  评论这张
 
阅读(2446)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017